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

Source Code for Module Gnumed.business.gmClinicalRecord

   1  """GNUmed clinical patient record. 
   2   
   3  This is a clinical record object intended to let a useful 
   4  client-side API crystallize from actual use in true XP fashion. 
   5   
   6  Make sure to call set_func_ask_user() and set_encounter_ttl() 
   7  early on in your code (before cClinicalRecord.__init__() is 
   8  called for the first time). 
   9  """ 
  10  #============================================================ 
  11  __version__ = "$Revision: 1.308 $" 
  12  __author__ = "K.Hilbert <Karsten.Hilbert@gmx.net>" 
  13  __license__ = "GPL" 
  14   
  15  #=================================================== 
  16  # TODO 
  17  # Basically we'll probably have to: 
  18  # 
  19  # a) serialize access to re-getting data from the cache so 
  20  #   that later-but-concurrent cache accesses spin until 
  21  #   the first one completes the refetch from the database 
  22  # 
  23  # b) serialize access to the cache per-se such that cache 
  24  #    flushes vs. cache regets happen atomically (where 
  25  #    flushes would abort/restart current regets) 
  26  #=================================================== 
  27   
  28  # standard libs 
  29  import sys, string, time, copy, locale 
  30   
  31   
  32  # 3rd party 
  33  import logging 
  34   
  35   
  36  if __name__ == '__main__': 
  37          sys.path.insert(0, '../../') 
  38          from Gnumed.pycommon import gmLog2, gmDateTime, gmI18N 
  39          gmI18N.activate_locale() 
  40          gmI18N.install_domain() 
  41          gmDateTime.init() 
  42   
  43  from Gnumed.pycommon import gmExceptions, gmPG2, gmDispatcher, gmI18N, gmCfg, gmTools, gmDateTime 
  44   
  45  from Gnumed.business import gmAllergy 
  46  from Gnumed.business import gmPathLab 
  47  from Gnumed.business import gmClinNarrative 
  48  from Gnumed.business import gmEMRStructItems 
  49  from Gnumed.business import gmMedication 
  50  from Gnumed.business import gmVaccination 
  51  from Gnumed.business import gmFamilyHistory 
  52  from Gnumed.business.gmDemographicRecord import get_occupations 
  53   
  54   
  55  _log = logging.getLogger('gm.emr') 
  56  _log.debug(__version__) 
  57   
  58  _me = None 
  59  _here = None 
  60  #============================================================ 
  61  # helper functions 
  62  #------------------------------------------------------------ 
  63  _func_ask_user = None 
  64   
65 -def set_func_ask_user(a_func = None):
66 if not callable(a_func): 67 _log.error('[%] not callable, not setting _func_ask_user', a_func) 68 return False 69 70 _log.debug('setting _func_ask_user to [%s]', a_func) 71 72 global _func_ask_user 73 _func_ask_user = a_func
74 75 #============================================================
76 -class cClinicalRecord(object):
77 78 _clin_root_item_children_union_query = None 79
80 - def __init__(self, aPKey = None):
81 """Fails if 82 83 - no connection to database possible 84 - patient referenced by aPKey does not exist 85 """ 86 self.pk_patient = aPKey # == identity.pk == primary key 87 88 # log access to patient record (HIPAA, for example) 89 cmd = u'SELECT gm.log_access2emr(%(todo)s)' 90 args = {'todo': u'patient [%s]' % aPKey} 91 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 92 93 from Gnumed.business import gmSurgery, gmStaff 94 global _me 95 if _me is None: 96 _me = gmStaff.gmCurrentProvider() 97 global _here 98 if _here is None: 99 _here = gmSurgery.gmCurrentPractice() 100 101 # ........................................... 102 # this is a hack to speed up get_encounters() 103 clin_root_item_children = gmPG2.get_child_tables('clin', 'clin_root_item') 104 if cClinicalRecord._clin_root_item_children_union_query is None: 105 union_phrase = u""" 106 SELECT fk_encounter from 107 %s.%s cn 108 inner join 109 (SELECT pk FROM clin.episode ep WHERE ep.fk_health_issue in %%s) as epi 110 on (cn.fk_episode = epi.pk) 111 """ 112 cClinicalRecord._clin_root_item_children_union_query = u'union\n'.join ( 113 [ union_phrase % (child[0], child[1]) for child in clin_root_item_children ] 114 ) 115 # ........................................... 116 117 self.__db_cache = {} 118 119 # load current or create new encounter 120 if _func_ask_user is None: 121 _log.error('[_func_ask_user] is None') 122 print "*** GNUmed [%s]: _func_ask_user is not set ***" % self.__class__.__name__ 123 self.remove_empty_encounters() 124 self.__encounter = None 125 if not self.__initiate_active_encounter(): 126 raise gmExceptions.ConstructorError, "cannot activate an encounter for patient [%s]" % aPKey 127 128 gmAllergy.ensure_has_allergy_state(encounter = self.current_encounter['pk_encounter']) 129 130 # register backend notification interests 131 # (keep this last so we won't hang on threads when 132 # failing this constructor for other reasons ...) 133 if not self._register_interests(): 134 raise gmExceptions.ConstructorError, "cannot register signal interests" 135 136 _log.debug('Instantiated clinical record for patient [%s].' % self.pk_patient)
137 #--------------------------------------------------------
138 - def __del__(self):
139 pass
140 #--------------------------------------------------------
141 - def cleanup(self):
142 _log.debug('cleaning up after clinical record for patient [%s]' % self.pk_patient) 143 144 return True
145 #-------------------------------------------------------- 146 # messaging 147 #--------------------------------------------------------
148 - def _register_interests(self):
149 gmDispatcher.connect(signal = u'encounter_mod_db', receiver = self.db_callback_encounter_mod_db) 150 151 return True
152 #--------------------------------------------------------
153 - def db_callback_encounter_mod_db(self, **kwds):
154 155 # get the current encounter as an extra instance 156 # from the database to check for changes 157 curr_enc_in_db = gmEMRStructItems.cEncounter(aPK_obj = self.current_encounter['pk_encounter']) 158 159 # the encounter just retrieved and the active encounter 160 # have got the same transaction ID so there's no change 161 # in the database, there could be a local change in 162 # the active encounter but that doesn't matter 163 # THIS DOES NOT WORK 164 # if curr_enc_in_db['xmin_encounter'] == self.current_encounter['xmin_encounter']: 165 # return True 166 167 # there must have been a change to the active encounter 168 # committed to the database from elsewhere, 169 # we must fail propagating the change, however, if 170 # there are local changes 171 if self.current_encounter.is_modified(): 172 _log.debug('unsaved changes in active encounter, cannot switch to another one') 173 raise ValueError('unsaved changes in active encounter, cannot switch to another one') 174 175 # there was a change in the database from elsewhere, 176 # locally, however, we don't have any changes, therefore 177 # we can propagate the remote change locally without 178 # losing anything 179 _log.debug('active encounter modified remotely, reloading and announcing the modification') 180 self.current_encounter.refetch_payload() 181 gmDispatcher.send(u'current_encounter_modified') 182 183 return True
184 #--------------------------------------------------------
185 - def db_callback_vaccs_modified(self, **kwds):
186 return True
187 #--------------------------------------------------------
188 - def _health_issues_modified(self):
189 try: 190 del self.__db_cache['health issues'] 191 except KeyError: 192 pass 193 return 1
194 #--------------------------------------------------------
196 # try: 197 # del self.__db_cache['episodes'] 198 # except KeyError: 199 # pass 200 return 1
201 #--------------------------------------------------------
202 - def _clin_item_modified(self):
203 _log.debug('DB: clin_root_item modification')
204 #-------------------------------------------------------- 205 # API: family history 206 #--------------------------------------------------------
207 - def get_family_history(self, episodes=None, issues=None):
208 fhx = gmFamilyHistory.get_family_history ( 209 order_by = u'l10n_relation, condition', 210 patient = self.pk_patient 211 ) 212 213 if episodes is not None: 214 fhx = filter(lambda f: f['pk_episode'] in episodes, fhx) 215 216 if issues is not None: 217 fhx = filter(lambda f: f['pk_health_issue'] in issues, fhx) 218 219 return fhx
220 #--------------------------------------------------------
221 - def add_family_history(self, episode=None, condition=None, relation=None):
222 return gmFamilyHistory.create_family_history ( 223 encounter = self.current_encounter['pk_encounter'], 224 episode = episode, 225 condition = condition, 226 relation = relation 227 )
228 #-------------------------------------------------------- 229 # API: performed procedures 230 #--------------------------------------------------------
231 - def get_performed_procedures(self, episodes=None, issues=None):
232 233 procs = gmEMRStructItems.get_performed_procedures(patient = self.pk_patient) 234 235 if episodes is not None: 236 procs = filter(lambda p: p['pk_episode'] in episodes, procs) 237 238 if issues is not None: 239 procs = filter(lambda p: p['pk_health_issue'] in issues, procs) 240 241 return procs
242 243 performed_procedures = property(get_performed_procedures, lambda x:x) 244 #-------------------------------------------------------- 247 #--------------------------------------------------------
248 - def add_performed_procedure(self, episode=None, location=None, hospital_stay=None, procedure=None):
249 return gmEMRStructItems.create_performed_procedure ( 250 encounter = self.current_encounter['pk_encounter'], 251 episode = episode, 252 location = location, 253 hospital_stay = hospital_stay, 254 procedure = procedure 255 )
256 #-------------------------------------------------------- 257 # API: hospitalizations 258 #--------------------------------------------------------
259 - def get_hospital_stays(self, episodes=None, issues=None, ongoing_only=False):
260 stays = gmEMRStructItems.get_patient_hospital_stays(patient = self.pk_patient, ongoing_only = ongoing_only) 261 if episodes is not None: 262 stays = filter(lambda s: s['pk_episode'] in episodes, stays) 263 if issues is not None: 264 stays = filter(lambda s: s['pk_health_issue'] in issues, stays) 265 return stays
266 267 hospital_stays = property(get_hospital_stays, lambda x:x) 268 #--------------------------------------------------------
269 - def get_latest_hospital_stay(self):
271 #--------------------------------------------------------
272 - def add_hospital_stay(self, episode=None):
273 return gmEMRStructItems.create_hospital_stay ( 274 encounter = self.current_encounter['pk_encounter'], 275 episode = episode 276 )
277 #--------------------------------------------------------
278 - def get_hospital_stay_stats_by_hospital(self, cover_period=None):
279 args = {'pat': self.pk_patient, 'range': cover_period} 280 where_parts = [u'pk_patient = %(pat)s'] 281 if cover_period is not None: 282 where_parts.append(u'discharge > (now() - %(range)s)') 283 284 cmd = u""" 285 SELECT hospital, count(1) AS frequency 286 FROM clin.v_pat_hospital_stays 287 WHERE 288 %s 289 GROUP BY hospital 290 ORDER BY frequency DESC 291 """ % u' AND '.join(where_parts) 292 293 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 294 return rows
295 #-------------------------------------------------------- 296 # API: narrative 297 #--------------------------------------------------------
298 - def add_notes(self, notes=None, episode=None, encounter=None):
299 300 enc = gmTools.coalesce ( 301 encounter, 302 self.current_encounter['pk_encounter'] 303 ) 304 305 for note in notes: 306 success, data = gmClinNarrative.create_clin_narrative ( 307 narrative = note[1], 308 soap_cat = note[0], 309 episode_id = episode, 310 encounter_id = enc 311 ) 312 313 return True
314 #--------------------------------------------------------
315 - def add_clin_narrative(self, note='', soap_cat='s', episode=None):
316 if note.strip() == '': 317 _log.info('will not create empty clinical note') 318 return None 319 status, data = gmClinNarrative.create_clin_narrative ( 320 narrative = note, 321 soap_cat = soap_cat, 322 episode_id = episode['pk_episode'], 323 encounter_id = self.current_encounter['pk_encounter'] 324 ) 325 if not status: 326 _log.error(str(data)) 327 return None 328 return data
329 #--------------------------------------------------------
330 - def get_clin_narrative(self, since=None, until=None, encounters=None, episodes=None, issues=None, soap_cats=None, providers=None):
331 """Get SOAP notes pertinent to this encounter. 332 333 since 334 - initial date for narrative items 335 until 336 - final date for narrative items 337 encounters 338 - list of encounters whose narrative are to be retrieved 339 episodes 340 - list of episodes whose narrative are to be retrieved 341 issues 342 - list of health issues whose narrative are to be retrieved 343 soap_cats 344 - list of SOAP categories of the narrative to be retrieved 345 """ 346 cmd = u""" 347 SELECT cvpn.*, (SELECT rank FROM clin.soap_cat_ranks WHERE soap_cat = cvpn.soap_cat) as soap_rank 348 from clin.v_pat_narrative cvpn 349 WHERE pk_patient = %s 350 order by date, soap_rank 351 """ 352 353 ########################## 354 # support row_version in narrative for display in tree 355 356 rows, idx = gmPG2.run_ro_queries(queries=[{'cmd': cmd, 'args': [self.pk_patient]}], get_col_idx=True) 357 358 filtered_narrative = [ gmClinNarrative.cNarrative(row = {'pk_field': 'pk_narrative', 'idx': idx, 'data': row}) for row in rows ] 359 360 if since is not None: 361 filtered_narrative = filter(lambda narr: narr['date'] >= since, filtered_narrative) 362 363 if until is not None: 364 filtered_narrative = filter(lambda narr: narr['date'] < until, filtered_narrative) 365 366 if issues is not None: 367 filtered_narrative = filter(lambda narr: narr['pk_health_issue'] in issues, filtered_narrative) 368 369 if episodes is not None: 370 filtered_narrative = filter(lambda narr: narr['pk_episode'] in episodes, filtered_narrative) 371 372 if encounters is not None: 373 filtered_narrative = filter(lambda narr: narr['pk_encounter'] in encounters, filtered_narrative) 374 375 if soap_cats is not None: 376 soap_cats = map(lambda c: c.lower(), soap_cats) 377 filtered_narrative = filter(lambda narr: narr['soap_cat'] in soap_cats, filtered_narrative) 378 379 if providers is not None: 380 filtered_narrative = filter(lambda narr: narr['provider'] in providers, filtered_narrative) 381 382 return filtered_narrative
383 #--------------------------------------------------------
384 - def get_as_journal(self, since=None, until=None, encounters=None, episodes=None, issues=None, soap_cats=None, providers=None, order_by=None, time_range=None):
385 return gmClinNarrative.get_as_journal ( 386 patient = self.pk_patient, 387 since = since, 388 until = until, 389 encounters = encounters, 390 episodes = episodes, 391 issues = issues, 392 soap_cats = soap_cats, 393 providers = providers, 394 order_by = order_by, 395 time_range = time_range 396 )
397 #--------------------------------------------------------
398 - def search_narrative_simple(self, search_term=''):
399 400 search_term = search_term.strip() 401 if search_term == '': 402 return [] 403 404 cmd = u""" 405 SELECT 406 *, 407 coalesce((SELECT description FROM clin.episode WHERE pk = vn4s.pk_episode), vn4s.src_table) 408 as episode, 409 coalesce((SELECT description FROM clin.health_issue WHERE pk = vn4s.pk_health_issue), vn4s.src_table) 410 as health_issue, 411 (SELECT started FROM clin.encounter WHERE pk = vn4s.pk_encounter) 412 as encounter_started, 413 (SELECT last_affirmed FROM clin.encounter WHERE pk = vn4s.pk_encounter) 414 as encounter_ended, 415 (SELECT _(description) FROM clin.encounter_type WHERE pk = (SELECT fk_type FROM clin.encounter WHERE pk = vn4s.pk_encounter)) 416 as encounter_type 417 from clin.v_narrative4search vn4s 418 WHERE 419 pk_patient = %(pat)s and 420 vn4s.narrative ~ %(term)s 421 order by 422 encounter_started 423 """ # case sensitive 424 rows, idx = gmPG2.run_ro_queries(queries = [ 425 {'cmd': cmd, 'args': {'pat': self.pk_patient, 'term': search_term}} 426 ]) 427 return rows
428 #--------------------------------------------------------
429 - def get_text_dump_old(self):
430 # don't know how to invalidate this by means of 431 # a notify without catching notifies from *all* 432 # child tables, the best solution would be if 433 # inserts in child tables would also fire triggers 434 # of ancestor tables, but oh well, 435 # until then the text dump will not be cached ... 436 try: 437 return self.__db_cache['text dump old'] 438 except KeyError: 439 pass 440 # not cached so go get it 441 fields = [ 442 "to_char(modified_when, 'YYYY-MM-DD @ HH24:MI') as modified_when", 443 'modified_by', 444 'clin_when', 445 "case is_modified when false then '%s' else '%s' end as modified_string" % (_('original entry'), _('modified entry')), 446 'pk_item', 447 'pk_encounter', 448 'pk_episode', 449 'pk_health_issue', 450 'src_table' 451 ] 452 cmd = "SELECT %s FROM clin.v_pat_items WHERE pk_patient=%%s order by src_table, clin_when" % string.join(fields, ', ') 453 ro_conn = self._conn_pool.GetConnection('historica') 454 curs = ro_conn.cursor() 455 if not gmPG2.run_query(curs, None, cmd, self.pk_patient): 456 _log.error('cannot load item links for patient [%s]' % self.pk_patient) 457 curs.close() 458 return None 459 rows = curs.fetchall() 460 view_col_idx = gmPG2.get_col_indices(curs) 461 462 # aggregate by src_table for item retrieval 463 items_by_table = {} 464 for item in rows: 465 src_table = item[view_col_idx['src_table']] 466 pk_item = item[view_col_idx['pk_item']] 467 if not items_by_table.has_key(src_table): 468 items_by_table[src_table] = {} 469 items_by_table[src_table][pk_item] = item 470 471 # get mapping for issue/episode IDs 472 issues = self.get_health_issues() 473 issue_map = {} 474 for issue in issues: 475 issue_map[issue['pk']] = issue['description'] 476 episodes = self.get_episodes() 477 episode_map = {} 478 for episode in episodes: 479 episode_map[episode['pk_episode']] = episode['description'] 480 emr_data = {} 481 # get item data from all source tables 482 for src_table in items_by_table.keys(): 483 item_ids = items_by_table[src_table].keys() 484 # we don't know anything about the columns of 485 # the source tables but, hey, this is a dump 486 if len(item_ids) == 0: 487 _log.info('no items in table [%s] ?!?' % src_table) 488 continue 489 elif len(item_ids) == 1: 490 cmd = "SELECT * FROM %s WHERE pk_item=%%s order by modified_when" % src_table 491 if not gmPG2.run_query(curs, None, cmd, item_ids[0]): 492 _log.error('cannot load items from table [%s]' % src_table) 493 # skip this table 494 continue 495 elif len(item_ids) > 1: 496 cmd = "SELECT * FROM %s WHERE pk_item in %%s order by modified_when" % src_table 497 if not gmPG.run_query(curs, None, cmd, (tuple(item_ids),)): 498 _log.error('cannot load items from table [%s]' % src_table) 499 # skip this table 500 continue 501 rows = curs.fetchall() 502 table_col_idx = gmPG.get_col_indices(curs) 503 # format per-table items 504 for row in rows: 505 # FIXME: make this get_pkey_name() 506 pk_item = row[table_col_idx['pk_item']] 507 view_row = items_by_table[src_table][pk_item] 508 age = view_row[view_col_idx['age']] 509 # format metadata 510 try: 511 episode_name = episode_map[view_row[view_col_idx['pk_episode']]] 512 except: 513 episode_name = view_row[view_col_idx['pk_episode']] 514 try: 515 issue_name = issue_map[view_row[view_col_idx['pk_health_issue']]] 516 except: 517 issue_name = view_row[view_col_idx['pk_health_issue']] 518 519 if not emr_data.has_key(age): 520 emr_data[age] = [] 521 522 emr_data[age].append( 523 _('%s: encounter (%s)') % ( 524 view_row[view_col_idx['clin_when']], 525 view_row[view_col_idx['pk_encounter']] 526 ) 527 ) 528 emr_data[age].append(_('health issue: %s') % issue_name) 529 emr_data[age].append(_('episode : %s') % episode_name) 530 # format table specific data columns 531 # - ignore those, they are metadata, some 532 # are in clin.v_pat_items data already 533 cols2ignore = [ 534 'pk_audit', 'row_version', 'modified_when', 'modified_by', 535 'pk_item', 'id', 'fk_encounter', 'fk_episode' 536 ] 537 col_data = [] 538 for col_name in table_col_idx.keys(): 539 if col_name in cols2ignore: 540 continue 541 emr_data[age].append("=> %s:" % col_name) 542 emr_data[age].append(row[table_col_idx[col_name]]) 543 emr_data[age].append("----------------------------------------------------") 544 emr_data[age].append("-- %s from table %s" % ( 545 view_row[view_col_idx['modified_string']], 546 src_table 547 )) 548 emr_data[age].append("-- written %s by %s" % ( 549 view_row[view_col_idx['modified_when']], 550 view_row[view_col_idx['modified_by']] 551 )) 552 emr_data[age].append("----------------------------------------------------") 553 curs.close() 554 self._conn_pool.ReleaseConnection('historica') 555 return emr_data
556 #--------------------------------------------------------
557 - def get_text_dump(self, since=None, until=None, encounters=None, episodes=None, issues=None):
558 # don't know how to invalidate this by means of 559 # a notify without catching notifies from *all* 560 # child tables, the best solution would be if 561 # inserts in child tables would also fire triggers 562 # of ancestor tables, but oh well, 563 # until then the text dump will not be cached ... 564 try: 565 return self.__db_cache['text dump'] 566 except KeyError: 567 pass 568 # not cached so go get it 569 # -- get the data -- 570 fields = [ 571 'age', 572 "to_char(modified_when, 'YYYY-MM-DD @ HH24:MI') as modified_when", 573 'modified_by', 574 'clin_when', 575 "case is_modified when false then '%s' else '%s' end as modified_string" % (_('original entry'), _('modified entry')), 576 'pk_item', 577 'pk_encounter', 578 'pk_episode', 579 'pk_health_issue', 580 'src_table' 581 ] 582 select_from = "SELECT %s FROM clin.v_pat_items" % ', '.join(fields) 583 # handle constraint conditions 584 where_snippets = [] 585 params = {} 586 where_snippets.append('pk_patient=%(pat_id)s') 587 params['pat_id'] = self.pk_patient 588 if not since is None: 589 where_snippets.append('clin_when >= %(since)s') 590 params['since'] = since 591 if not until is None: 592 where_snippets.append('clin_when <= %(until)s') 593 params['until'] = until 594 # FIXME: these are interrelated, eg if we constrain encounter 595 # we automatically constrain issue/episode, so handle that, 596 # encounters 597 if not encounters is None and len(encounters) > 0: 598 params['enc'] = encounters 599 if len(encounters) > 1: 600 where_snippets.append('fk_encounter in %(enc)s') 601 else: 602 where_snippets.append('fk_encounter=%(enc)s') 603 # episodes 604 if not episodes is None and len(episodes) > 0: 605 params['epi'] = episodes 606 if len(episodes) > 1: 607 where_snippets.append('fk_episode in %(epi)s') 608 else: 609 where_snippets.append('fk_episode=%(epi)s') 610 # health issues 611 if not issues is None and len(issues) > 0: 612 params['issue'] = issues 613 if len(issues) > 1: 614 where_snippets.append('fk_health_issue in %(issue)s') 615 else: 616 where_snippets.append('fk_health_issue=%(issue)s') 617 618 where_clause = ' and '.join(where_snippets) 619 order_by = 'order by src_table, age' 620 cmd = "%s WHERE %s %s" % (select_from, where_clause, order_by) 621 622 rows, view_col_idx = gmPG.run_ro_query('historica', cmd, 1, params) 623 if rows is None: 624 _log.error('cannot load item links for patient [%s]' % self.pk_patient) 625 return None 626 627 # -- sort the data -- 628 # FIXME: by issue/encounter/episode, eg formatting 629 # aggregate by src_table for item retrieval 630 items_by_table = {} 631 for item in rows: 632 src_table = item[view_col_idx['src_table']] 633 pk_item = item[view_col_idx['pk_item']] 634 if not items_by_table.has_key(src_table): 635 items_by_table[src_table] = {} 636 items_by_table[src_table][pk_item] = item 637 638 # get mapping for issue/episode IDs 639 issues = self.get_health_issues() 640 issue_map = {} 641 for issue in issues: 642 issue_map[issue['pk_health_issue']] = issue['description'] 643 episodes = self.get_episodes() 644 episode_map = {} 645 for episode in episodes: 646 episode_map[episode['pk_episode']] = episode['description'] 647 emr_data = {} 648 # get item data from all source tables 649 ro_conn = self._conn_pool.GetConnection('historica') 650 curs = ro_conn.cursor() 651 for src_table in items_by_table.keys(): 652 item_ids = items_by_table[src_table].keys() 653 # we don't know anything about the columns of 654 # the source tables but, hey, this is a dump 655 if len(item_ids) == 0: 656 _log.info('no items in table [%s] ?!?' % src_table) 657 continue 658 elif len(item_ids) == 1: 659 cmd = "SELECT * FROM %s WHERE pk_item=%%s order by modified_when" % src_table 660 if not gmPG.run_query(curs, None, cmd, item_ids[0]): 661 _log.error('cannot load items from table [%s]' % src_table) 662 # skip this table 663 continue 664 elif len(item_ids) > 1: 665 cmd = "SELECT * FROM %s WHERE pk_item in %%s order by modified_when" % src_table 666 if not gmPG.run_query(curs, None, cmd, (tuple(item_ids),)): 667 _log.error('cannot load items from table [%s]' % src_table) 668 # skip this table 669 continue 670 rows = curs.fetchall() 671 table_col_idx = gmPG.get_col_indices(curs) 672 # format per-table items 673 for row in rows: 674 # FIXME: make this get_pkey_name() 675 pk_item = row[table_col_idx['pk_item']] 676 view_row = items_by_table[src_table][pk_item] 677 age = view_row[view_col_idx['age']] 678 # format metadata 679 try: 680 episode_name = episode_map[view_row[view_col_idx['pk_episode']]] 681 except: 682 episode_name = view_row[view_col_idx['pk_episode']] 683 try: 684 issue_name = issue_map[view_row[view_col_idx['pk_health_issue']]] 685 except: 686 issue_name = view_row[view_col_idx['pk_health_issue']] 687 688 if not emr_data.has_key(age): 689 emr_data[age] = [] 690 691 emr_data[age].append( 692 _('%s: encounter (%s)') % ( 693 view_row[view_col_idx['clin_when']], 694 view_row[view_col_idx['pk_encounter']] 695 ) 696 ) 697 emr_data[age].append(_('health issue: %s') % issue_name) 698 emr_data[age].append(_('episode : %s') % episode_name) 699 # format table specific data columns 700 # - ignore those, they are metadata, some 701 # are in clin.v_pat_items data already 702 cols2ignore = [ 703 'pk_audit', 'row_version', 'modified_when', 'modified_by', 704 'pk_item', 'id', 'fk_encounter', 'fk_episode', 'pk' 705 ] 706 col_data = [] 707 for col_name in table_col_idx.keys(): 708 if col_name in cols2ignore: 709 continue 710 emr_data[age].append("=> %s: %s" % (col_name, row[table_col_idx[col_name]])) 711 emr_data[age].append("----------------------------------------------------") 712 emr_data[age].append("-- %s from table %s" % ( 713 view_row[view_col_idx['modified_string']], 714 src_table 715 )) 716 emr_data[age].append("-- written %s by %s" % ( 717 view_row[view_col_idx['modified_when']], 718 view_row[view_col_idx['modified_by']] 719 )) 720 emr_data[age].append("----------------------------------------------------") 721 curs.close() 722 return emr_data
723 #--------------------------------------------------------
724 - def get_patient_ID(self):
725 return self.pk_patient
726 #--------------------------------------------------------
727 - def get_statistics(self):
728 union_query = u'\n union all\n'.join ([ 729 u""" 730 SELECT (( 731 -- all relevant health issues + active episodes WITH health issue 732 SELECT COUNT(1) 733 FROM clin.v_problem_list 734 WHERE 735 pk_patient = %(pat)s 736 AND 737 pk_health_issue is not null 738 ) + ( 739 -- active episodes WITHOUT health issue 740 SELECT COUNT(1) 741 FROM clin.v_problem_list 742 WHERE 743 pk_patient = %(pat)s 744 AND 745 pk_health_issue is null 746 ))""", 747 u'SELECT count(1) FROM clin.encounter WHERE fk_patient = %(pat)s', 748 u'SELECT count(1) FROM clin.v_pat_items WHERE pk_patient = %(pat)s', 749 u'SELECT count(1) FROM blobs.v_doc_med WHERE pk_patient = %(pat)s', 750 u'SELECT count(1) FROM clin.v_test_results WHERE pk_patient = %(pat)s', 751 u'SELECT count(1) FROM clin.v_pat_hospital_stays WHERE pk_patient = %(pat)s', 752 u'SELECT count(1) FROM clin.v_pat_procedures WHERE pk_patient = %(pat)s', 753 # active and approved substances == medication 754 u""" 755 SELECT count(1) 756 from clin.v_pat_substance_intake 757 WHERE 758 pk_patient = %(pat)s 759 and is_currently_active in (null, true) 760 and intake_is_approved_of in (null, true)""", 761 u'SELECT count(1) FROM clin.v_pat_vaccinations WHERE pk_patient = %(pat)s' 762 ]) 763 764 rows, idx = gmPG2.run_ro_queries ( 765 queries = [{'cmd': union_query, 'args': {'pat': self.pk_patient}}], 766 get_col_idx = False 767 ) 768 769 stats = dict ( 770 problems = rows[0][0], 771 encounters = rows[1][0], 772 items = rows[2][0], 773 documents = rows[3][0], 774 results = rows[4][0], 775 stays = rows[5][0], 776 procedures = rows[6][0], 777 active_drugs = rows[7][0], 778 vaccinations = rows[8][0] 779 ) 780 781 return stats
782 #--------------------------------------------------------
783 - def format_statistics(self):
784 return _( 785 'Medical problems: %(problems)s\n' 786 'Total encounters: %(encounters)s\n' 787 'Total EMR entries: %(items)s\n' 788 'Active medications: %(active_drugs)s\n' 789 'Documents: %(documents)s\n' 790 'Test results: %(results)s\n' 791 'Hospitalizations: %(stays)s\n' 792 'Procedures: %(procedures)s\n' 793 'Vaccinations: %(vaccinations)s' 794 ) % self.get_statistics()
795 #--------------------------------------------------------
796 - def format_summary(self, dob=None):
797 798 stats = self.get_statistics() 799 first = self.get_first_encounter() 800 last = self.get_last_encounter() 801 probs = self.get_problems() 802 803 txt = u'' 804 if len(probs) > 0: 805 txt += _(' %s known problems, clinically relevant thereof:\n') % stats['problems'] 806 else: 807 txt += _(' %s known problems\n') % stats['problems'] 808 for prob in probs: 809 if not prob['clinically_relevant']: 810 continue 811 txt += u' \u00BB%s\u00AB (%s)\n' % ( 812 prob['problem'], 813 gmTools.bool2subst(prob['problem_active'], _('active'), _('inactive')) 814 ) 815 txt += u'\n' 816 txt += _(' %s encounters from %s to %s\n') % ( 817 stats['encounters'], 818 first['started'].strftime('%x').decode(gmI18N.get_encoding()), 819 last['started'].strftime('%x').decode(gmI18N.get_encoding()) 820 ) 821 txt += _(' %s active medications\n') % stats['active_drugs'] 822 txt += _(' %s documents\n') % stats['documents'] 823 txt += _(' %s test results\n') % stats['results'] 824 txt += _(' %s hospitalizations') % stats['stays'] 825 if stats['stays'] == 0: 826 txt += u'\n' 827 else: 828 txt += _(', most recently:\n%s\n') % self.get_latest_hospital_stay().format(left_margin = 3) 829 # FIXME: perhaps only count "ongoing ones" 830 txt += _(' %s performed procedures') % stats['procedures'] 831 if stats['procedures'] == 0: 832 txt += u'\n' 833 else: 834 txt += _(', most recently:\n%s\n') % self.get_latest_performed_procedure().format(left_margin = 3) 835 836 txt += u'\n' 837 txt += _('Allergies and Intolerances\n') 838 839 allg_state = self.allergy_state 840 txt += (u' ' + allg_state.state_string) 841 if allg_state['last_confirmed'] is not None: 842 txt += (_(' (last confirmed %s)') % allg_state['last_confirmed'].strftime('%x').decode(gmI18N.get_encoding())) 843 txt += u'\n' 844 txt += gmTools.coalesce(allg_state['comment'], u'', u' %s\n') 845 for allg in self.get_allergies(): 846 txt += u' %s: %s\n' % ( 847 allg['descriptor'], 848 gmTools.coalesce(allg['reaction'], _('unknown reaction')) 849 ) 850 851 txt += u'\n' 852 txt += _('Family History') 853 txt += u'\n' 854 fhx = self.get_family_history() 855 for f in fhx: 856 txt += u'%s\n' % f.format(left_margin = 1) 857 858 txt += u'\n' 859 txt += _('Occupations') 860 txt += u'\n' 861 jobs = get_occupations(pk_identity = self.pk_patient) 862 for job in jobs: 863 txt += u' %s%s\n' % ( 864 job['l10n_occupation'], 865 gmTools.coalesce(job['activities'], u'', u': %s') 866 ) 867 868 txt += u'\n' 869 txt += _('Vaccinations') 870 txt += u'\n' 871 vaccs = self.get_latest_vaccinations() 872 inds = sorted(vaccs.keys()) 873 for ind in inds: 874 ind_count, vacc = vaccs[ind] 875 if dob is None: 876 age_given = u'' 877 else: 878 age_given = u' @ %s' % gmDateTime.format_apparent_age_medically(gmDateTime.calculate_apparent_age ( 879 start = dob, 880 end = vacc['date_given'] 881 )) 882 since = _('%s ago') % gmDateTime.format_interval_medically(vacc['interval_since_given']) 883 txt += u' %s (%s%s): %s%s (%s %s%s%s)\n' % ( 884 ind, 885 gmTools.u_sum, 886 ind_count, 887 #vacc['date_given'].strftime('%b %Y').decode(gmI18N.get_encoding()), 888 since, 889 age_given, 890 vacc['vaccine'], 891 gmTools.u_left_double_angle_quote, 892 vacc['batch_no'], 893 gmTools.u_right_double_angle_quote 894 ) 895 896 return txt
897 #-------------------------------------------------------- 898 # API: allergy 899 #--------------------------------------------------------
900 - def get_allergies(self, remove_sensitivities=False, since=None, until=None, encounters=None, episodes=None, issues=None, ID_list=None):
901 """Retrieves patient allergy items. 902 903 remove_sensitivities 904 - retrieve real allergies only, without sensitivities 905 since 906 - initial date for allergy items 907 until 908 - final date for allergy items 909 encounters 910 - list of encounters whose allergies are to be retrieved 911 episodes 912 - list of episodes whose allergies are to be retrieved 913 issues 914 - list of health issues whose allergies are to be retrieved 915 """ 916 cmd = u"SELECT * FROM clin.v_pat_allergies WHERE pk_patient=%s order by descriptor" 917 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_patient]}], get_col_idx = True) 918 allergies = [] 919 for r in rows: 920 allergies.append(gmAllergy.cAllergy(row = {'data': r, 'idx': idx, 'pk_field': 'pk_allergy'})) 921 922 # ok, let's constrain our list 923 filtered_allergies = [] 924 filtered_allergies.extend(allergies) 925 926 if ID_list is not None: 927 filtered_allergies = filter(lambda allg: allg['pk_allergy'] in ID_list, filtered_allergies) 928 if len(filtered_allergies) == 0: 929 _log.error('no allergies of list [%s] found for patient [%s]' % (str(ID_list), self.pk_patient)) 930 # better fail here contrary to what we do elsewhere 931 return None 932 else: 933 return filtered_allergies 934 935 if remove_sensitivities: 936 filtered_allergies = filter(lambda allg: allg['type'] == 'allergy', filtered_allergies) 937 if since is not None: 938 filtered_allergies = filter(lambda allg: allg['date'] >= since, filtered_allergies) 939 if until is not None: 940 filtered_allergies = filter(lambda allg: allg['date'] < until, filtered_allergies) 941 if issues is not None: 942 filtered_allergies = filter(lambda allg: allg['pk_health_issue'] in issues, filtered_allergies) 943 if episodes is not None: 944 filtered_allergies = filter(lambda allg: allg['pk_episode'] in episodes, filtered_allergies) 945 if encounters is not None: 946 filtered_allergies = filter(lambda allg: allg['pk_encounter'] in encounters, filtered_allergies) 947 948 return filtered_allergies
949 #--------------------------------------------------------
950 - def add_allergy(self, allergene=None, allg_type=None, encounter_id=None, episode_id=None):
951 if encounter_id is None: 952 encounter_id = self.current_encounter['pk_encounter'] 953 954 if episode_id is None: 955 issue = self.add_health_issue(issue_name = _('Allergies/Intolerances')) 956 epi = self.add_episode(episode_name = _('Allergy detail: %s') % allergene, pk_health_issue = issue['pk_health_issue']) 957 episode_id = epi['pk_episode'] 958 959 new_allergy = gmAllergy.create_allergy ( 960 allergene = allergene, 961 allg_type = allg_type, 962 encounter_id = encounter_id, 963 episode_id = episode_id 964 ) 965 966 return new_allergy
967 #--------------------------------------------------------
968 - def delete_allergy(self, pk_allergy=None):
969 cmd = u'delete FROM clin.allergy WHERE pk=%(pk_allg)s' 970 args = {'pk_allg': pk_allergy} 971 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
972 #--------------------------------------------------------
973 - def is_allergic_to(self, atcs=None, inns=None, brand=None):
974 """Cave: only use with one potential allergic agent 975 otherwise you won't know which of the agents the allergy is to.""" 976 977 # we don't know the state 978 if self.allergy_state is None: 979 return None 980 981 # we know there's no allergies 982 if self.allergy_state == 0: 983 return False 984 985 args = { 986 'atcs': atcs, 987 'inns': inns, 988 'brand': brand, 989 'pat': self.pk_patient 990 } 991 allergenes = [] 992 where_parts = [] 993 994 if len(atcs) == 0: 995 atcs = None 996 if atcs is not None: 997 where_parts.append(u'atc_code in %(atcs)s') 998 if len(inns) == 0: 999 inns = None 1000 if inns is not None: 1001 where_parts.append(u'generics in %(inns)s') 1002 allergenes.extend(inns) 1003 if brand is not None: 1004 where_parts.append(u'substance = %(brand)s') 1005 allergenes.append(brand) 1006 1007 if len(allergenes) != 0: 1008 where_parts.append(u'allergene in %(allgs)s') 1009 args['allgs'] = tuple(allergenes) 1010 1011 cmd = u""" 1012 SELECT * FROM clin.v_pat_allergies 1013 WHERE 1014 pk_patient = %%(pat)s 1015 AND ( %s )""" % u' OR '.join(where_parts) 1016 1017 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 1018 1019 if len(rows) == 0: 1020 return False 1021 1022 return gmAllergy.cAllergy(row = {'data': rows[0], 'idx': idx, 'pk_field': 'pk_allergy'})
1023 #--------------------------------------------------------
1024 - def _set_allergy_state(self, state):
1025 1026 if state not in gmAllergy.allergy_states: 1027 raise ValueError('[%s].__set_allergy_state(): <state> must be one of %s' % (self.__class__.__name__, gmAllergy.allergy_states)) 1028 1029 allg_state = gmAllergy.ensure_has_allergy_state(encounter = self.current_encounter['pk_encounter']) 1030 allg_state['has_allergy'] = state 1031 allg_state.save_payload() 1032 return True
1033
1034 - def _get_allergy_state(self):
1035 return gmAllergy.ensure_has_allergy_state(encounter = self.current_encounter['pk_encounter'])
1036 1037 allergy_state = property(_get_allergy_state, _set_allergy_state) 1038 #-------------------------------------------------------- 1039 # API: episodes 1040 #--------------------------------------------------------
1041 - def get_episodes(self, id_list=None, issues=None, open_status=None, order_by=None, unlinked_only=False):
1042 """Fetches from backend patient episodes. 1043 1044 id_list - Episodes' PKs list 1045 issues - Health issues' PKs list to filter episodes by 1046 open_status - return all (None) episodes, only open (True) or closed (False) one(s) 1047 """ 1048 if (unlinked_only is True) and (issues is not None): 1049 raise ValueError('<unlinked_only> cannot be TRUE if <issues> is not None') 1050 1051 if order_by is None: 1052 order_by = u'' 1053 else: 1054 order_by = u'ORDER BY %s' % order_by 1055 1056 args = { 1057 'pat': self.pk_patient, 1058 'open': open_status 1059 } 1060 where_parts = [u'pk_patient = %(pat)s'] 1061 1062 if open_status is not None: 1063 where_parts.append(u'episode_open IS %(open)s') 1064 1065 if unlinked_only: 1066 where_parts.append(u'pk_health_issue is NULL') 1067 1068 if issues is not None: 1069 where_parts.append(u'pk_health_issue IN %(issues)s') 1070 args['issues'] = tuple(issues) 1071 1072 if id_list is not None: 1073 where_parts.append(u'pk_episode IN %(epis)s') 1074 args['epis'] = tuple(id_list) 1075 1076 cmd = u"SELECT * FROM clin.v_pat_episodes WHERE %s %s" % ( 1077 u' AND '.join(where_parts), 1078 order_by 1079 ) 1080 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 1081 1082 return [ gmEMRStructItems.cEpisode(row = {'data': r, 'idx': idx, 'pk_field': 'pk_episode'}) for r in rows ]
1083 1084 episodes = property(get_episodes, lambda x:x) 1085 #------------------------------------------------------------------
1086 - def get_unlinked_episodes(self, open_status=None, order_by=None):
1087 return self.get_episodes(open_status = open_status, order_by = order_by, unlinked_only = True)
1088 1089 unlinked_episodes = property(get_unlinked_episodes, lambda x:x) 1090 #------------------------------------------------------------------
1091 - def get_episodes_by_encounter(self, pk_encounter=None):
1092 cmd = u"""SELECT distinct pk_episode 1093 from clin.v_pat_items 1094 WHERE pk_encounter=%(enc)s and pk_patient=%(pat)s""" 1095 args = { 1096 'enc': gmTools.coalesce(pk_encounter, self.current_encounter['pk_encounter']), 1097 'pat': self.pk_patient 1098 } 1099 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}]) 1100 if len(rows) == 0: 1101 return [] 1102 epis = [] 1103 for row in rows: 1104 epis.append(row[0]) 1105 return self.get_episodes(id_list=epis)
1106 #------------------------------------------------------------------
1107 - def add_episode(self, episode_name=None, pk_health_issue=None, is_open=False):
1108 """Add episode 'episode_name' for a patient's health issue. 1109 1110 - silently returns if episode already exists 1111 """ 1112 episode = gmEMRStructItems.create_episode ( 1113 pk_health_issue = pk_health_issue, 1114 episode_name = episode_name, 1115 is_open = is_open, 1116 encounter = self.current_encounter['pk_encounter'] 1117 ) 1118 return episode
1119 #--------------------------------------------------------
1120 - def get_most_recent_episode(self, issue=None):
1121 # try to find the episode with the most recently modified clinical item 1122 1123 issue_where = gmTools.coalesce(issue, u'', u'and pk_health_issue = %(issue)s') 1124 1125 cmd = u""" 1126 SELECT pk 1127 from clin.episode 1128 WHERE pk = ( 1129 SELECT distinct on(pk_episode) pk_episode 1130 from clin.v_pat_items 1131 WHERE 1132 pk_patient = %%(pat)s 1133 and 1134 modified_when = ( 1135 SELECT max(vpi.modified_when) 1136 from clin.v_pat_items vpi 1137 WHERE vpi.pk_patient = %%(pat)s 1138 ) 1139 %s 1140 -- guard against several episodes created at the same moment of time 1141 limit 1 1142 )""" % issue_where 1143 rows, idx = gmPG2.run_ro_queries(queries = [ 1144 {'cmd': cmd, 'args': {'pat': self.pk_patient, 'issue': issue}} 1145 ]) 1146 if len(rows) != 0: 1147 return gmEMRStructItems.cEpisode(aPK_obj=rows[0][0]) 1148 1149 # no clinical items recorded, so try to find 1150 # the youngest episode for this patient 1151 cmd = u""" 1152 SELECT vpe0.pk_episode 1153 from 1154 clin.v_pat_episodes vpe0 1155 WHERE 1156 vpe0.pk_patient = %%(pat)s 1157 and 1158 vpe0.episode_modified_when = ( 1159 SELECT max(vpe1.episode_modified_when) 1160 from clin.v_pat_episodes vpe1 1161 WHERE vpe1.pk_episode = vpe0.pk_episode 1162 ) 1163 %s""" % issue_where 1164 rows, idx = gmPG2.run_ro_queries(queries = [ 1165 {'cmd': cmd, 'args': {'pat': self.pk_patient, 'issue': issue}} 1166 ]) 1167 if len(rows) != 0: 1168 return gmEMRStructItems.cEpisode(aPK_obj=rows[0][0]) 1169 1170 return None
1171 #--------------------------------------------------------
1172 - def episode2problem(self, episode=None):
1173 return gmEMRStructItems.episode2problem(episode=episode)
1174 #-------------------------------------------------------- 1175 # API: problems 1176 #--------------------------------------------------------
1177 - def get_problems(self, episodes=None, issues=None, include_closed_episodes=False, include_irrelevant_issues=False):
1178 """Retrieve a patient's problems. 1179 1180 "Problems" are the UNION of: 1181 1182 - issues which are .clinically_relevant 1183 - episodes which are .is_open 1184 1185 Therefore, both an issue and the open episode 1186 thereof can each be listed as a problem. 1187 1188 include_closed_episodes/include_irrelevant_issues will 1189 include those -- which departs from the definition of 1190 the problem list being "active" items only ... 1191 1192 episodes - episodes' PKs to filter problems by 1193 issues - health issues' PKs to filter problems by 1194 """ 1195 # FIXME: this could use a good measure of streamlining, probably 1196 1197 args = {'pat': self.pk_patient} 1198 1199 cmd = u"""SELECT pk_health_issue, pk_episode FROM clin.v_problem_list WHERE pk_patient = %(pat)s ORDER BY problem""" 1200 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 1201 1202 # Instantiate problem items 1203 problems = [] 1204 for row in rows: 1205 pk_args = { 1206 u'pk_patient': self.pk_patient, 1207 u'pk_health_issue': row['pk_health_issue'], 1208 u'pk_episode': row['pk_episode'] 1209 } 1210 problems.append(gmEMRStructItems.cProblem(aPK_obj = pk_args, try_potential_problems = False)) 1211 1212 # include non-problems ? 1213 other_rows = [] 1214 if include_closed_episodes: 1215 cmd = u"""SELECT pk_health_issue, pk_episode FROM clin.v_potential_problem_list WHERE pk_patient = %(pat)s and type = 'episode'""" 1216 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 1217 other_rows.extend(rows) 1218 1219 if include_irrelevant_issues: 1220 cmd = u"""SELECT pk_health_issue, pk_episode FROM clin.v_potential_problem_list WHERE pk_patient = %(pat)s and type = 'health issue'""" 1221 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 1222 other_rows.extend(rows) 1223 1224 if len(other_rows) > 0: 1225 for row in other_rows: 1226 pk_args = { 1227 u'pk_patient': self.pk_patient, 1228 u'pk_health_issue': row['pk_health_issue'], 1229 u'pk_episode': row['pk_episode'] 1230 } 1231 problems.append(gmEMRStructItems.cProblem(aPK_obj = pk_args, try_potential_problems = True)) 1232 1233 # filter ? 1234 if (episodes is None) and (issues is None): 1235 return problems 1236 1237 # filter 1238 if issues is not None: 1239 problems = filter(lambda epi: epi['pk_health_issue'] in issues, problems) 1240 if episodes is not None: 1241 problems = filter(lambda epi: epi['pk_episode'] in episodes, problems) 1242 1243 return problems
1244 #--------------------------------------------------------
1245 - def problem2episode(self, problem=None):
1246 return gmEMRStructItems.problem2episode(problem = problem)
1247 #--------------------------------------------------------
1248 - def problem2issue(self, problem=None):
1249 return gmEMRStructItems.problem2issue(problem = problem)
1250 #--------------------------------------------------------
1251 - def reclass_problem(self, problem):
1252 return gmEMRStructItems.reclass_problem(problem = problem)
1253 #-------------------------------------------------------- 1254 # API: health issues 1255 #--------------------------------------------------------
1256 - def get_health_issues(self, id_list = None):
1257 1258 cmd = u"SELECT *, xmin_health_issue FROM clin.v_health_issues WHERE pk_patient = %(pat)s" 1259 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'pat': self.pk_patient}}], get_col_idx = True) 1260 issues = [ gmEMRStructItems.cHealthIssue(row = {'idx': idx, 'data': r, 'pk_field': 'pk_health_issue'}) for r in rows ] 1261 1262 if id_list is None: 1263 return issues 1264 1265 if len(id_list) == 0: 1266 raise ValueError('id_list to filter by is empty, most likely a programming error') 1267 1268 filtered_issues = [] 1269 for issue in issues: 1270 if issue['pk_health_issue'] in id_list: 1271 filtered_issues.append(issue) 1272 1273 return filtered_issues
1274 1275 health_issues = property(get_health_issues, lambda x:x) 1276 #------------------------------------------------------------------
1277 - def add_health_issue(self, issue_name=None):
1278 """Adds patient health issue.""" 1279 return gmEMRStructItems.create_health_issue ( 1280 description = issue_name, 1281 encounter = self.current_encounter['pk_encounter'], 1282 patient = self.pk_patient 1283 )
1284 #--------------------------------------------------------
1285 - def health_issue2problem(self, issue=None):
1286 return gmEMRStructItems.health_issue2problem(issue = issue)
1287 #-------------------------------------------------------- 1288 # API: substance intake 1289 #--------------------------------------------------------
1290 - def get_current_substance_intake(self, include_inactive=True, include_unapproved=False, order_by=None, episodes=None, issues=None):
1291 1292 where_parts = [u'pk_patient = %(pat)s'] 1293 1294 if not include_inactive: 1295 where_parts.append(u'is_currently_active in (true, null)') 1296 1297 if not include_unapproved: 1298 where_parts.append(u'intake_is_approved_of in (true, null)') 1299 1300 if order_by is None: 1301 order_by = u'' 1302 else: 1303 order_by = u'order by %s' % order_by 1304 1305 cmd = u"SELECT * FROM clin.v_pat_substance_intake WHERE %s %s" % ( 1306 u'\nand '.join(where_parts), 1307 order_by 1308 ) 1309 1310 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'pat': self.pk_patient}}], get_col_idx = True) 1311 1312 meds = [ gmMedication.cSubstanceIntakeEntry(row = {'idx': idx, 'data': r, 'pk_field': 'pk_substance_intake'}) for r in rows ] 1313 1314 if episodes is not None: 1315 meds = filter(lambda s: s['pk_episode'] in episodes, meds) 1316 1317 if issues is not None: 1318 meds = filter(lambda s: s['pk_health_issue'] in issues, meds) 1319 1320 return meds
1321 #--------------------------------------------------------
1322 - def add_substance_intake(self, pk_substance=None, pk_component=None, episode=None, preparation=None):
1323 return gmMedication.create_substance_intake ( 1324 pk_substance = pk_substance, 1325 pk_component = pk_component, 1326 encounter = self.current_encounter['pk_encounter'], 1327 episode = episode, 1328 preparation = preparation 1329 )
1330 #--------------------------------------------------------
1331 - def substance_intake_exists(self, pk_component=None, pk_substance=None):
1332 return gmMedication.substance_intake_exists ( 1333 pk_component = pk_component, 1334 pk_substance = pk_substance, 1335 pk_identity = self.pk_patient 1336 )
1337 #-------------------------------------------------------- 1338 # API: vaccinations 1339 #--------------------------------------------------------
1340 - def add_vaccination(self, episode=None, vaccine=None, batch_no=None):
1341 return gmVaccination.create_vaccination ( 1342 encounter = self.current_encounter['pk_encounter'], 1343 episode = episode, 1344 vaccine = vaccine, 1345 batch_no = batch_no 1346 )
1347 #--------------------------------------------------------
1348 - def get_latest_vaccinations(self, episodes=None, issues=None):
1349 """Returns latest given vaccination for each vaccinated indication. 1350 1351 as a dict {'l10n_indication': cVaccination instance} 1352 1353 Note that this will produce duplicate vaccination instances on combi-indication vaccines ! 1354 """ 1355 # find the PKs 1356 args = {'pat': self.pk_patient} 1357 where_parts = [u'pk_patient = %(pat)s'] 1358 1359 if (episodes is not None) and (len(episodes) > 0): 1360 where_parts.append(u'pk_episode IN %(epis)s') 1361 args['epis'] = tuple(episodes) 1362 1363 if (issues is not None) and (len(issues) > 0): 1364 where_parts.append(u'pk_episode IN (select pk from clin.episode where fk_health_issue IN %(issues)s)') 1365 args['issues'] = tuple(issues) 1366 1367 cmd = u'SELECT pk_vaccination, l10n_indication, indication_count FROM clin.v_pat_last_vacc4indication WHERE %s' % u'\nAND '.join(where_parts) 1368 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 1369 1370 # none found 1371 if len(rows) == 0: 1372 return {} 1373 1374 vpks = [ ind['pk_vaccination'] for ind in rows ] 1375 vinds = [ ind['l10n_indication'] for ind in rows ] 1376 ind_counts = [ ind['indication_count'] for ind in rows ] 1377 1378 # turn them into vaccinations 1379 cmd = gmVaccination.sql_fetch_vaccination % u'pk_vaccination IN %(pks)s' 1380 args = {'pks': tuple(vpks)} 1381 rows, row_idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 1382 1383 vaccs = {} 1384 for idx in range(len(vpks)): 1385 pk = vpks[idx] 1386 ind_count = ind_counts[idx] 1387 for r in rows: 1388 if r['pk_vaccination'] == pk: 1389 vaccs[vinds[idx]] = (ind_count, gmVaccination.cVaccination(row = {'idx': row_idx, 'data': r, 'pk_field': 'pk_vaccination'})) 1390 1391 return vaccs
1392 #--------------------------------------------------------
1393 - def get_vaccinations(self, order_by=None, episodes=None, issues=None, encounters=None):
1394 1395 args = {'pat': self.pk_patient} 1396 where_parts = [u'pk_patient = %(pat)s'] 1397 1398 if order_by is None: 1399 order_by = u'' 1400 else: 1401 order_by = u'ORDER BY %s' % order_by 1402 1403 if (episodes is not None) and (len(episodes) > 0): 1404 where_parts.append(u'pk_episode IN %(epis)s') 1405 args['epis'] = tuple(episodes) 1406 1407 if (issues is not None) and (len(issues) > 0): 1408 where_parts.append(u'pk_episode IN (SELECT pk FROM clin.episode WHERE fk_health_issue IN %(issues)s)') 1409 args['issues'] = tuple(issues) 1410 1411 if (encounters is not None) and (len(encounters) > 0): 1412 where_parts.append(u'pk_encounter IN %(encs)s') 1413 args['encs'] = tuple(encounters) 1414 1415 cmd = u'%s %s' % ( 1416 gmVaccination.sql_fetch_vaccination % u'\nAND '.join(where_parts), 1417 order_by 1418 ) 1419 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 1420 vaccs = [ gmVaccination.cVaccination(row = {'idx': idx, 'data': r, 'pk_field': 'pk_vaccination'}) for r in rows ] 1421 1422 return vaccs
1423 1424 vaccinations = property(get_vaccinations, lambda x:x) 1425 #-------------------------------------------------------- 1426 # old/obsolete: 1427 #--------------------------------------------------------
1428 - def get_scheduled_vaccination_regimes(self, ID=None, indications=None):
1429 """Retrieves vaccination regimes the patient is on. 1430 1431 optional: 1432 * ID - PK of the vaccination regime 1433 * indications - indications we want to retrieve vaccination 1434 regimes for, must be primary language, not l10n_indication 1435 """ 1436 # FIXME: use course, not regime 1437 try: 1438 self.__db_cache['vaccinations']['scheduled regimes'] 1439 except KeyError: 1440 # retrieve vaccination regimes definitions 1441 self.__db_cache['vaccinations']['scheduled regimes'] = [] 1442 cmd = """SELECT distinct on(pk_course) pk_course 1443 FROM clin.v_vaccs_scheduled4pat 1444 WHERE pk_patient=%s""" 1445 rows = gmPG.run_ro_query('historica', cmd, None, self.pk_patient) 1446 if rows is None: 1447 _log.error('cannot retrieve scheduled vaccination courses') 1448 del self.__db_cache['vaccinations']['scheduled regimes'] 1449 return None 1450 # Instantiate vaccination items and keep cache 1451 for row in rows: 1452 self.__db_cache['vaccinations']['scheduled regimes'].append(gmVaccination.cVaccinationCourse(aPK_obj=row[0])) 1453 1454 # ok, let's constrain our list 1455 filtered_regimes = [] 1456 filtered_regimes.extend(self.__db_cache['vaccinations']['scheduled regimes']) 1457 if ID is not None: 1458 filtered_regimes = filter(lambda regime: regime['pk_course'] == ID, filtered_regimes) 1459 if len(filtered_regimes) == 0: 1460 _log.error('no vaccination course [%s] found for patient [%s]' % (ID, self.pk_patient)) 1461 return [] 1462 else: 1463 return filtered_regimes[0] 1464 if indications is not None: 1465 filtered_regimes = filter(lambda regime: regime['indication'] in indications, filtered_regimes) 1466 1467 return filtered_regimes
1468 #-------------------------------------------------------- 1469 # def get_vaccinated_indications(self): 1470 # """Retrieves patient vaccinated indications list. 1471 # 1472 # Note that this does NOT rely on the patient being on 1473 # some schedule or other but rather works with what the 1474 # patient has ACTUALLY been vaccinated against. This is 1475 # deliberate ! 1476 # """ 1477 # # most likely, vaccinations will be fetched close 1478 # # by so it makes sense to count on the cache being 1479 # # filled (or fill it for nearby use) 1480 # vaccinations = self.get_vaccinations() 1481 # if vaccinations is None: 1482 # _log.error('cannot load vaccinated indications for patient [%s]' % self.pk_patient) 1483 # return (False, [[_('ERROR: cannot retrieve vaccinated indications'), _('ERROR: cannot retrieve vaccinated indications')]]) 1484 # if len(vaccinations) == 0: 1485 # return (True, [[_('no vaccinations recorded'), _('no vaccinations recorded')]]) 1486 # v_indications = [] 1487 # for vacc in vaccinations: 1488 # tmp = [vacc['indication'], vacc['l10n_indication']] 1489 # # remove duplicates 1490 # if tmp in v_indications: 1491 # continue 1492 # v_indications.append(tmp) 1493 # return (True, v_indications) 1494 #--------------------------------------------------------
1495 - def get_vaccinations_old(self, ID=None, indications=None, since=None, until=None, encounters=None, episodes=None, issues=None):
1496 """Retrieves list of vaccinations the patient has received. 1497 1498 optional: 1499 * ID - PK of a vaccination 1500 * indications - indications we want to retrieve vaccination 1501 items for, must be primary language, not l10n_indication 1502 * since - initial date for allergy items 1503 * until - final date for allergy items 1504 * encounters - list of encounters whose allergies are to be retrieved 1505 * episodes - list of episodes whose allergies are to be retrieved 1506 * issues - list of health issues whose allergies are to be retrieved 1507 """ 1508 try: 1509 self.__db_cache['vaccinations']['vaccinated'] 1510 except KeyError: 1511 self.__db_cache['vaccinations']['vaccinated'] = [] 1512 # Important fetch ordering by indication, date to know if a vaccination is booster 1513 cmd= """SELECT * FROM clin.v_pat_vaccinations4indication 1514 WHERE pk_patient=%s 1515 order by indication, date""" 1516 rows, idx = gmPG.run_ro_query('historica', cmd, True, self.pk_patient) 1517 if rows is None: 1518 _log.error('cannot load given vaccinations for patient [%s]' % self.pk_patient) 1519 del self.__db_cache['vaccinations']['vaccinated'] 1520 return None 1521 # Instantiate vaccination items 1522 vaccs_by_ind = {} 1523 for row in rows: 1524 vacc_row = { 1525 'pk_field': 'pk_vaccination', 1526 'idx': idx, 1527 'data': row 1528 } 1529 vacc = gmVaccination.cVaccination(row=vacc_row) 1530 self.__db_cache['vaccinations']['vaccinated'].append(vacc) 1531 # keep them, ordered by indication 1532 try: 1533 vaccs_by_ind[vacc['indication']].append(vacc) 1534 except KeyError: 1535 vaccs_by_ind[vacc['indication']] = [vacc] 1536 1537 # calculate sequence number and is_booster 1538 for ind in vaccs_by_ind.keys(): 1539 vacc_regimes = self.get_scheduled_vaccination_regimes(indications = [ind]) 1540 for vacc in vaccs_by_ind[ind]: 1541 # due to the "order by indication, date" the vaccinations are in the 1542 # right temporal order inside the indication-keyed dicts 1543 seq_no = vaccs_by_ind[ind].index(vacc) + 1 1544 vacc['seq_no'] = seq_no 1545 # if no active schedule for indication we cannot 1546 # check for booster status (eg. seq_no > max_shot) 1547 if (vacc_regimes is None) or (len(vacc_regimes) == 0): 1548 continue 1549 if seq_no > vacc_regimes[0]['shots']: 1550 vacc['is_booster'] = True 1551 del vaccs_by_ind 1552 1553 # ok, let's constrain our list 1554 filtered_shots = [] 1555 filtered_shots.extend(self.__db_cache['vaccinations']['vaccinated']) 1556 if ID is not None: 1557 filtered_shots = filter(lambda shot: shot['pk_vaccination'] == ID, filtered_shots) 1558 if len(filtered_shots) == 0: 1559 _log.error('no vaccination [%s] found for patient [%s]' % (ID, self.pk_patient)) 1560 return None 1561 else: 1562 return filtered_shots[0] 1563 if since is not None: 1564 filtered_shots = filter(lambda shot: shot['date'] >= since, filtered_shots) 1565 if until is not None: 1566 filtered_shots = filter(lambda shot: shot['date'] < until, filtered_shots) 1567 if issues is not None: 1568 filtered_shots = filter(lambda shot: shot['pk_health_issue'] in issues, filtered_shots) 1569 if episodes is not None: 1570 filtered_shots = filter(lambda shot: shot['pk_episode'] in episodes, filtered_shots) 1571 if encounters is not None: 1572 filtered_shots = filter(lambda shot: shot['pk_encounter'] in encounters, filtered_shots) 1573 if indications is not None: 1574 filtered_shots = filter(lambda shot: shot['indication'] in indications, filtered_shots) 1575 return filtered_shots
1576 #--------------------------------------------------------
1577 - def get_scheduled_vaccinations(self, indications=None):
1578 """Retrieves vaccinations scheduled for a regime a patient is on. 1579 1580 The regime is referenced by its indication (not l10n) 1581 1582 * indications - List of indications (not l10n) of regimes we want scheduled 1583 vaccinations to be fetched for 1584 """ 1585 try: 1586 self.__db_cache['vaccinations']['scheduled'] 1587 except KeyError: 1588 self.__db_cache['vaccinations']['scheduled'] = [] 1589 cmd = """SELECT * FROM clin.v_vaccs_scheduled4pat WHERE pk_patient=%s""" 1590 rows, idx = gmPG.run_ro_query('historica', cmd, True, self.pk_patient) 1591 if rows is None: 1592 _log.error('cannot load scheduled vaccinations for patient [%s]' % self.pk_patient) 1593 del self.__db_cache['vaccinations']['scheduled'] 1594 return None 1595 # Instantiate vaccination items 1596 for row in rows: 1597 vacc_row = { 1598 'pk_field': 'pk_vacc_def', 1599 'idx': idx, 1600 'data': row 1601 } 1602 self.__db_cache['vaccinations']['scheduled'].append(gmVaccination.cScheduledVaccination(row = vacc_row)) 1603 1604 # ok, let's constrain our list 1605 if indications is None: 1606 return self.__db_cache['vaccinations']['scheduled'] 1607 filtered_shots = [] 1608 filtered_shots.extend(self.__db_cache['vaccinations']['scheduled']) 1609 filtered_shots = filter(lambda shot: shot['indication'] in indications, filtered_shots) 1610 return filtered_shots
1611 #--------------------------------------------------------
1612 - def get_missing_vaccinations(self, indications=None):
1613 try: 1614 self.__db_cache['vaccinations']['missing'] 1615 except KeyError: 1616 self.__db_cache['vaccinations']['missing'] = {} 1617 # 1) non-booster 1618 self.__db_cache['vaccinations']['missing']['due'] = [] 1619 # get list of (indication, seq_no) tuples 1620 cmd = "SELECT indication, seq_no FROM clin.v_pat_missing_vaccs WHERE pk_patient=%s" 1621 rows = gmPG.run_ro_query('historica', cmd, None, self.pk_patient) 1622 if rows is None: 1623 _log.error('error loading (indication, seq_no) for due/overdue vaccinations for patient [%s]' % self.pk_patient) 1624 return None 1625 pk_args = {'pat_id': self.pk_patient} 1626 if rows is not None: 1627 for row in rows: 1628 pk_args['indication'] = row[0] 1629 pk_args['seq_no'] = row[1] 1630 self.__db_cache['vaccinations']['missing']['due'].append(gmVaccination.cMissingVaccination(aPK_obj=pk_args)) 1631 1632 # 2) boosters 1633 self.__db_cache['vaccinations']['missing']['boosters'] = [] 1634 # get list of indications 1635 cmd = "SELECT indication, seq_no FROM clin.v_pat_missing_boosters WHERE pk_patient=%s" 1636 rows = gmPG.run_ro_query('historica', cmd, None, self.pk_patient) 1637 if rows is None: 1638 _log.error('error loading indications for missing boosters for patient [%s]' % self.pk_patient) 1639 return None 1640 pk_args = {'pat_id': self.pk_patient} 1641 if rows is not None: 1642 for row in rows: 1643 pk_args['indication'] = row[0] 1644 self.__db_cache['vaccinations']['missing']['boosters'].append(gmVaccination.cMissingBooster(aPK_obj=pk_args)) 1645 1646 # if any filters ... 1647 if indications is None: 1648 return self.__db_cache['vaccinations']['missing'] 1649 if len(indications) == 0: 1650 return self.__db_cache['vaccinations']['missing'] 1651 # ... apply them 1652 filtered_shots = { 1653 'due': [], 1654 'boosters': [] 1655 } 1656 for due_shot in self.__db_cache['vaccinations']['missing']['due']: 1657 if due_shot['indication'] in indications: #and due_shot not in filtered_shots['due']: 1658 filtered_shots['due'].append(due_shot) 1659 for due_shot in self.__db_cache['vaccinations']['missing']['boosters']: 1660 if due_shot['indication'] in indications: #and due_shot not in filtered_shots['boosters']: 1661 filtered_shots['boosters'].append(due_shot) 1662 return filtered_shots
1663 #------------------------------------------------------------------ 1664 # API: encounters 1665 #------------------------------------------------------------------
1666 - def _get_current_encounter(self):
1667 return self.__encounter
1668
1669 - def _set_current_encounter(self, encounter):
1670 1671 # first ever setting ? 1672 if self.__encounter is None: 1673 _log.debug('first setting of active encounter in this clinical record instance') 1674 else: 1675 _log.debug('switching of active encounter') 1676 # fail if the currently active encounter has unsaved changes 1677 if self.__encounter.is_modified(): 1678 _log.debug('unsaved changes in active encounter, cannot switch to another one') 1679 raise ValueError('unsaved changes in active encounter, cannot switch to another one') 1680 1681 # set the currently active encounter and announce that change 1682 if encounter['started'].strftime('%Y-%m-%d %H:%M') == encounter['last_affirmed'].strftime('%Y-%m-%d %H:%M'): 1683 now = gmDateTime.pydt_now_here() 1684 if now > encounter['started']: 1685 encounter['last_affirmed'] = now # this will trigger an "encounter_mod_db" 1686 encounter.save() 1687 self.__encounter = encounter 1688 gmDispatcher.send(u'current_encounter_switched') 1689 1690 return True
1691 1692 current_encounter = property(_get_current_encounter, _set_current_encounter) 1693 active_encounter = property(_get_current_encounter, _set_current_encounter) 1694 #------------------------------------------------------------------
1696 1697 # 1) "very recent" encounter recorded ? 1698 if self.__activate_very_recent_encounter(): 1699 return True 1700 1701 # 2) "fairly recent" encounter recorded ? 1702 if self.__activate_fairly_recent_encounter(): 1703 return True 1704 1705 # 3) start a completely new encounter 1706 self.start_new_encounter() 1707 return True
1708 #------------------------------------------------------------------
1710 """Try to attach to a "very recent" encounter if there is one. 1711 1712 returns: 1713 False: no "very recent" encounter, create new one 1714 True: success 1715 """ 1716 cfg_db = gmCfg.cCfgSQL() 1717 min_ttl = cfg_db.get2 ( 1718 option = u'encounter.minimum_ttl', 1719 workplace = _here.active_workplace, 1720 bias = u'user', 1721 default = u'1 hour 30 minutes' 1722 ) 1723 cmd = u""" 1724 SELECT pk_encounter 1725 FROM clin.v_most_recent_encounters 1726 WHERE 1727 pk_patient = %s 1728 and 1729 last_affirmed > (now() - %s::interval) 1730 ORDER BY 1731 last_affirmed DESC""" 1732 enc_rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_patient, min_ttl]}]) 1733 # none found 1734 if len(enc_rows) == 0: 1735 _log.debug('no <very recent> encounter (younger than [%s]) found' % min_ttl) 1736 return False 1737 # attach to existing 1738 self.current_encounter = gmEMRStructItems.cEncounter(aPK_obj=enc_rows[0][0]) 1739 _log.debug('"very recent" encounter [%s] found and re-activated' % enc_rows[0][0]) 1740 return True
1741 #------------------------------------------------------------------
1743 """Try to attach to a "fairly recent" encounter if there is one. 1744 1745 returns: 1746 False: no "fairly recent" encounter, create new one 1747 True: success 1748 """ 1749 if _func_ask_user is None: 1750 _log.debug('cannot ask user for guidance, not looking for fairly recent encounter') 1751 return False 1752 1753 cfg_db = gmCfg.cCfgSQL() 1754 min_ttl = cfg_db.get2 ( 1755 option = u'encounter.minimum_ttl', 1756 workplace = _here.active_workplace, 1757 bias = u'user', 1758 default = u'1 hour 30 minutes' 1759 ) 1760 max_ttl = cfg_db.get2 ( 1761 option = u'encounter.maximum_ttl', 1762 workplace = _here.active_workplace, 1763 bias = u'user', 1764 default = u'6 hours' 1765 ) 1766 cmd = u""" 1767 SELECT pk_encounter 1768 FROM clin.v_most_recent_encounters 1769 WHERE 1770 pk_patient=%s 1771 AND 1772 last_affirmed BETWEEN (now() - %s::interval) AND (now() - %s::interval) 1773 ORDER BY 1774 last_affirmed DESC""" 1775 enc_rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_patient, max_ttl, min_ttl]}]) 1776 # none found 1777 if len(enc_rows) == 0: 1778 _log.debug('no <fairly recent> encounter (between [%s] and [%s] old) found' % (min_ttl, max_ttl)) 1779 return False 1780 encounter = gmEMRStructItems.cEncounter(aPK_obj=enc_rows[0][0]) 1781 # ask user whether to attach or not 1782 cmd = u""" 1783 SELECT title, firstnames, lastnames, gender, dob 1784 FROM dem.v_basic_person WHERE pk_identity=%s""" 1785 pats, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_patient]}]) 1786 pat = pats[0] 1787 pat_str = u'%s %s %s (%s), %s [#%s]' % ( 1788 gmTools.coalesce(pat[0], u'')[:5], 1789 pat[1][:15], 1790 pat[2][:15], 1791 pat[3], 1792 pat[4].strftime('%x'), 1793 self.pk_patient 1794 ) 1795 enc = gmI18N.get_encoding() 1796 msg = _( 1797 '%s\n' 1798 '\n' 1799 "This patient's chart was worked on only recently:\n" 1800 '\n' 1801 ' %s %s - %s (%s)\n' 1802 '\n' 1803 ' Request: %s\n' 1804 ' Outcome: %s\n' 1805 '\n' 1806 'Do you want to continue that consultation\n' 1807 'or do you want to start a new one ?\n' 1808 ) % ( 1809 pat_str, 1810 encounter['started'].strftime('%x').decode(enc), 1811 encounter['started'].strftime('%H:%M'), encounter['last_affirmed'].strftime('%H:%M'), 1812 encounter['l10n_type'], 1813 gmTools.coalesce(encounter['reason_for_encounter'], _('none given')), 1814 gmTools.coalesce(encounter['assessment_of_encounter'], _('none given')), 1815 ) 1816 attach = False 1817 try: 1818 attach = _func_ask_user(msg = msg, caption = _('Starting patient encounter'), encounter = encounter) 1819 except: 1820 _log.exception('cannot ask user for guidance, not attaching to existing encounter') 1821 return False 1822 if not attach: 1823 return False 1824 1825 # attach to existing 1826 self.current_encounter = encounter 1827 1828 _log.debug('"fairly recent" encounter [%s] found and re-activated' % enc_rows[0][0]) 1829 return True
1830 #------------------------------------------------------------------
1831 - def start_new_encounter(self):
1832 cfg_db = gmCfg.cCfgSQL() 1833 # FIXME: look for MRU/MCU encounter type config here 1834 enc_type = cfg_db.get2 ( 1835 option = u'encounter.default_type', 1836 workplace = _here.active_workplace, 1837 bias = u'user', 1838 default = u'in surgery' 1839 ) 1840 self.current_encounter = gmEMRStructItems.create_encounter(fk_patient = self.pk_patient, enc_type = enc_type) 1841 _log.debug('new encounter [%s] initiated' % self.current_encounter['pk_encounter'])
1842 #------------------------------------------------------------------
1843 - def get_encounters(self, since=None, until=None, id_list=None, episodes=None, issues=None, skip_empty=False):
1844 """Retrieves patient's encounters. 1845 1846 id_list - PKs of encounters to fetch 1847 since - initial date for encounter items, DateTime instance 1848 until - final date for encounter items, DateTime instance 1849 episodes - PKs of the episodes the encounters belong to (many-to-many relation) 1850 issues - PKs of the health issues the encounters belong to (many-to-many relation) 1851 skip_empty - do NOT return those which do not have any of documents/clinical items/RFE/AOE 1852 1853 NOTE: if you specify *both* issues and episodes 1854 you will get the *aggregate* of all encounters even 1855 if the episodes all belong to the health issues listed. 1856 IOW, the issues broaden the episode list rather than 1857 the episode list narrowing the episodes-from-issues 1858 list. 1859 Rationale: If it was the other way round it would be 1860 redundant to specify the list of issues at all. 1861 """ 1862 where_parts = [u'c_vpe.pk_patient = %(pat)s'] 1863 args = {'pat': self.pk_patient} 1864 1865 if skip_empty: 1866 where_parts.append(u"""NOT ( 1867 gm.is_null_or_blank_string(c_vpe.reason_for_encounter) 1868 AND 1869 gm.is_null_or_blank_string(c_vpe.assessment_of_encounter) 1870 AND 1871 NOT EXISTS ( 1872 SELECT 1 FROM clin.v_pat_items c_vpi WHERE c_vpi.pk_patient = %(pat)s AND c_vpi.pk_encounter = c_vpe.pk_encounter 1873 UNION ALL 1874 SELECT 1 FROM blobs.v_doc_med b_vdm WHERE b_vdm.pk_patient = %(pat)s AND b_vdm.pk_encounter = c_vpe.pk_encounter 1875 ))""") 1876 1877 if since is not None: 1878 where_parts.append(u'c_vpe.started >= %(start)s') 1879 args['start'] = since 1880 1881 if until is not None: 1882 where_parts.append(u'c_vpe.last_affirmed <= %(end)s') 1883 args['end'] = since 1884 1885 cmd = u""" 1886 SELECT * 1887 FROM clin.v_pat_encounters c_vpe 1888 WHERE 1889 %s 1890 ORDER BY started 1891 """ % u' AND '.join(where_parts) 1892 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 1893 encounters = [ gmEMRStructItems.cEncounter(row = {'data': r, 'idx': idx, 'pk_field': 'pk_encounter'}) for r in rows ] 1894 1895 # we've got the encounters, start filtering 1896 filtered_encounters = [] 1897 filtered_encounters.extend(encounters) 1898 1899 if id_list is not None: 1900 filtered_encounters = filter(lambda enc: enc['pk_encounter'] in id_list, filtered_encounters) 1901 1902 if (issues is not None) and (len(issues) > 0): 1903 issues = tuple(issues) 1904 # however, this seems like the proper approach: 1905 # - find episodes corresponding to the health issues in question 1906 cmd = u"SELECT distinct pk FROM clin.episode WHERE fk_health_issue in %(issues)s" 1907 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'issues': issues}}]) 1908 epi_ids = map(lambda x:x[0], rows) 1909 if episodes is None: 1910 episodes = [] 1911 episodes.extend(epi_ids) 1912 1913 if (episodes is not None) and (len(episodes) > 0): 1914 episodes = tuple(episodes) 1915 # if the episodes to filter by belong to the patient in question so will 1916 # the encounters found with them - hence we don't need a WHERE on the patient ... 1917 cmd = u"SELECT distinct fk_encounter FROM clin.clin_root_item WHERE fk_episode in %(epis)s" 1918 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'epis': episodes}}]) 1919 enc_ids = map(lambda x:x[0], rows) 1920 filtered_encounters = filter(lambda enc: enc['pk_encounter'] in enc_ids, filtered_encounters) 1921 1922 return filtered_encounters
1923 #--------------------------------------------------------
1924 - def get_first_encounter(self, issue_id=None, episode_id=None):
1925 """Retrieves first encounter for a particular issue and/or episode. 1926 1927 issue_id - First encounter associated health issue 1928 episode - First encounter associated episode 1929 """ 1930 # FIXME: use direct query 1931 if issue_id is None: 1932 issues = None 1933 else: 1934 issues = [issue_id] 1935 1936 if episode_id is None: 1937 episodes = None 1938 else: 1939 episodes = [episode_id] 1940 1941 encounters = self.get_encounters(issues=issues, episodes=episodes) 1942 if len(encounters) == 0: 1943 return None 1944 1945 # FIXME: this does not scale particularly well, I assume 1946 encounters.sort(lambda x,y: cmp(x['started'], y['started'])) 1947 return encounters[0]
1948 #--------------------------------------------------------
1949 - def get_earliest_care_date(self):
1950 args = {'pat': self.pk_patient} 1951 cmd = u""" 1952 SELECT MIN(earliest) FROM ( 1953 ( 1954 SELECT MIN(episode_modified_when) AS earliest FROM clin.v_pat_episodes WHERE pk_patient = %(pat)s 1955 1956 ) UNION ALL ( 1957 1958 SELECT MIN(modified_when) AS earliest FROM clin.v_health_issues WHERE pk_patient = %(pat)s 1959 1960 ) UNION ALL ( 1961 1962 SELECT MIN(modified_when) AS earliest FROM clin.encounter WHERE fk_patient = %(pat)s 1963 1964 ) UNION ALL ( 1965 1966 SELECT MIN(started) AS earliest FROM clin.v_pat_encounters WHERE pk_patient = %(pat)s 1967 1968 ) UNION ALL ( 1969 1970 SELECT MIN(modified_when) AS earliest FROM clin.v_pat_items WHERE pk_patient = %(pat)s 1971 1972 ) UNION ALL ( 1973 1974 SELECT MIN(modified_when) AS earliest FROM clin.v_pat_allergy_state WHERE pk_patient = %(pat)s 1975 1976 ) UNION ALL ( 1977 1978 SELECT MIN(last_confirmed) AS earliest FROM clin.v_pat_allergy_state WHERE pk_patient = %(pat)s 1979 1980 ) 1981 ) AS candidates""" 1982 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 1983 return rows[0][0]
1984 1985 earliest_care_date = property(get_earliest_care_date, lambda x:x) 1986 #--------------------------------------------------------
1987 - def get_last_encounter(self, issue_id=None, episode_id=None):
1988 """Retrieves last encounter for a concrete issue and/or episode 1989 1990 issue_id - Last encounter associated health issue 1991 episode_id - Last encounter associated episode 1992 """ 1993 # FIXME: use direct query 1994 1995 if issue_id is None: 1996 issues = None 1997 else: 1998 issues = [issue_id] 1999 2000 if episode_id is None: 2001 episodes = None 2002 else: 2003 episodes = [episode_id] 2004 2005 encounters = self.get_encounters(issues=issues, episodes=episodes) 2006 if len(encounters) == 0: 2007 return None 2008 2009 # FIXME: this does not scale particularly well, I assume 2010 encounters.sort(lambda x,y: cmp(x['started'], y['started'])) 2011 return encounters[-1]
2012 2013 last_encounter = property(get_last_encounter, lambda x:x) 2014 #------------------------------------------------------------------
2015 - def get_encounter_stats_by_type(self, cover_period=None):
2016 args = {'pat': self.pk_patient, 'range': cover_period} 2017 where_parts = [u'pk_patient = %(pat)s'] 2018 if cover_period is not None: 2019 where_parts.append(u'last_affirmed > now() - %(range)s') 2020 2021 cmd = u""" 2022 SELECT l10n_type, count(1) AS frequency 2023 FROM clin.v_pat_encounters 2024 WHERE 2025 %s 2026 GROUP BY l10n_type 2027 ORDER BY frequency DESC 2028 """ % u' AND '.join(where_parts) 2029 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 2030 return rows
2031 #------------------------------------------------------------------
2032 - def get_last_but_one_encounter(self, issue_id=None, episode_id=None):
2033 2034 args = {'pat': self.pk_patient} 2035 2036 if (issue_id is None) and (episode_id is None): 2037 2038 cmd = u""" 2039 SELECT * FROM clin.v_pat_encounters 2040 WHERE pk_patient = %(pat)s 2041 ORDER BY started DESC 2042 LIMIT 2 2043 """ 2044 else: 2045 where_parts = [] 2046 2047 if issue_id is not None: 2048 where_parts.append(u'pk_health_issue = %(issue)s') 2049 args['issue'] = issue_id 2050 2051 if episode_id is not None: 2052 where_parts.append(u'pk_episode = %(epi)s') 2053 args['epi'] = episode_id 2054 2055 cmd = u""" 2056 SELECT * 2057 FROM clin.v_pat_encounters 2058 WHERE 2059 pk_patient = %%(pat)s 2060 AND 2061 pk_encounter IN ( 2062 SELECT distinct pk_encounter 2063 FROM clin.v_pat_narrative 2064 WHERE 2065 %s 2066 ) 2067 ORDER BY started DESC 2068 LIMIT 2 2069 """ % u' AND '.join(where_parts) 2070 2071 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 2072 2073 if len(rows) == 0: 2074 return None 2075 2076 # just one encounter within the above limits 2077 if len(rows) == 1: 2078 # is it the current encounter ? 2079 if rows[0]['pk_encounter'] == self.current_encounter['pk_encounter']: 2080 # yes 2081 return None 2082 # no 2083 return gmEMRStructItems.cEncounter(row = {'data': rows[0], 'idx': idx, 'pk_field': 'pk_encounter'}) 2084 2085 # more than one encounter 2086 if rows[0]['pk_encounter'] == self.current_encounter['pk_encounter']: 2087 return gmEMRStructItems.cEncounter(row = {'data': rows[1], 'idx': idx, 'pk_field': 'pk_encounter'}) 2088 2089 return gmEMRStructItems.cEncounter(row = {'data': rows[0], 'idx': idx, 'pk_field': 'pk_encounter'})
2090 #------------------------------------------------------------------
2091 - def remove_empty_encounters(self):
2092 cfg_db = gmCfg.cCfgSQL() 2093 ttl = cfg_db.get2 ( 2094 option = u'encounter.ttl_if_empty', 2095 workplace = _here.active_workplace, 2096 bias = u'user', 2097 default = u'1 week' 2098 ) 2099 2100 # # FIXME: this should be done async 2101 cmd = u"select clin.remove_old_empty_encounters(%(pat)s::integer, %(ttl)s::interval)" 2102 args = {'pat': self.pk_patient, 'ttl': ttl} 2103 try: 2104 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 2105 except: 2106 _log.exception('error deleting empty encounters') 2107 2108 return True
2109 #------------------------------------------------------------------ 2110 # API: measurements / test results 2111 #------------------------------------------------------------------
2112 - def get_most_recent_results(self, test_type=None, loinc=None, no_of_results=1):
2113 return gmPathLab.get_most_recent_results ( 2114 test_type = test_type, 2115 loinc = loinc, 2116 no_of_results = no_of_results, 2117 patient = self.pk_patient 2118 )
2119 #------------------------------------------------------------------
2120 - def get_unsigned_results(self, order_by=None):
2121 if order_by is None: 2122 order_by = u'' 2123 else: 2124 order_by = u'ORDER BY %s' % order_by 2125 cmd = u""" 2126 SELECT * FROM clin.v_test_results 2127 WHERE 2128 pk_patient = %%(pat)s 2129 AND 2130 reviewed IS FALSE 2131 %s""" % order_by 2132 args = {'pat': self.pk_patient} 2133 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 2134 return [ gmPathLab.cTestResult(row = {'pk_field': 'pk_test_result', 'idx': idx, 'data': r}) for r in rows ]
2135 #------------------------------------------------------------------ 2136 # FIXME: use psyopg2 dbapi extension of named cursors - they are *server* side !
2137 - def get_test_types_for_results(self):
2138 """Retrieve data about test types for which this patient has results.""" 2139 2140 cmd = u""" 2141 SELECT * FROM ( 2142 SELECT DISTINCT ON (pk_test_type) pk_test_type, clin_when, unified_name 2143 FROM clin.v_test_results 2144 WHERE pk_patient = %(pat)s 2145 ) AS foo 2146 ORDER BY clin_when desc, unified_name 2147 """ 2148 args = {'pat': self.pk_patient} 2149 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 2150 return [ gmPathLab.cUnifiedTestType(aPK_obj = row['pk_test_type']) for row in rows ]
2151 #------------------------------------------------------------------
2152 - def get_test_types_details(self):
2153 """Retrieve details on tests grouped under unified names for this patient's results.""" 2154 cmd = u""" 2155 SELECT * FROM clin.v_unified_test_types WHERE pk_test_type in ( 2156 SELECT distinct on (unified_name, unified_abbrev) pk_test_type 2157 from clin.v_test_results 2158 WHERE pk_patient = %(pat)s 2159 ) 2160 order by unified_name""" 2161 args = {'pat': self.pk_patient} 2162 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 2163 return rows, idx
2164 #------------------------------------------------------------------
2165 - def get_dates_for_results(self):
2166 """Get the dates for which we have results.""" 2167 cmd = u""" 2168 SELECT distinct on (cwhen) date_trunc('day', clin_when) as cwhen 2169 from clin.v_test_results 2170 WHERE pk_patient = %(pat)s 2171 order by cwhen desc""" 2172 args = {'pat': self.pk_patient} 2173 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 2174 return rows
2175 #------------------------------------------------------------------
2176 - def get_test_results_by_date(self, encounter=None, episodes=None):
2177 2178 cmd = u""" 2179 SELECT *, xmin_test_result FROM clin.v_test_results 2180 WHERE pk_patient = %(pat)s 2181 order by clin_when desc, pk_episode, unified_name""" 2182 args = {'pat': self.pk_patient} 2183 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 2184 2185 tests = [ gmPathLab.cTestResult(row = {'pk_field': 'pk_test_result', 'idx': idx, 'data': r}) for r in rows ] 2186 2187 if episodes is not None: 2188 tests = [ t for t in tests if t['pk_episode'] in episodes ] 2189 2190 if encounter is not None: 2191 tests = [ t for t in tests if t['pk_encounter'] == encounter ] 2192 2193 return tests
2194 #------------------------------------------------------------------
2195 - def add_test_result(self, episode=None, type=None, intended_reviewer=None, val_num=None, val_alpha=None, unit=None):
2196 2197 try: 2198 epi = int(episode) 2199 except: 2200 epi = episode['pk_episode'] 2201 2202 try: 2203 type = int(type) 2204 except: 2205 type = type['pk_test_type'] 2206 2207 if intended_reviewer is None: 2208 intended_reviewer = _me['pk_staff'] 2209 2210 tr = gmPathLab.create_test_result ( 2211 encounter = self.current_encounter['pk_encounter'], 2212 episode = epi, 2213 type = type, 2214 intended_reviewer = intended_reviewer, 2215 val_num = val_num, 2216 val_alpha = val_alpha, 2217 unit = unit 2218 ) 2219 2220 return tr
2221 #------------------------------------------------------------------
2222 - def get_bmi(self):
2223 2224 cfg_db = gmCfg.cCfgSQL() 2225 2226 mass_loincs = cfg_db.get2 ( 2227 option = u'lab.body_mass_loincs', 2228 workplace = _here.active_workplace, 2229 bias = u'user', 2230 default = [] 2231 ) 2232 2233 height_loincs = cfg_db.get2 ( 2234 option = u'lab.body_height_loincs', 2235 workplace = _here.active_workplace, 2236 bias = u'user', 2237 default = [] 2238 ) 2239 2240 return gmPathLab.calculate_bmi(mass = mass, height = height) # age = age
2241 #------------------------------------------------------------------ 2242 #------------------------------------------------------------------
2243 - def get_lab_results(self, limit=None, since=None, until=None, encounters=None, episodes=None, issues=None):
2244 """Retrieves lab result clinical items. 2245 2246 limit - maximum number of results to retrieve 2247 since - initial date 2248 until - final date 2249 encounters - list of encounters 2250 episodes - list of episodes 2251 issues - list of health issues 2252 """ 2253 try: 2254 return self.__db_cache['lab results'] 2255 except KeyError: 2256 pass 2257 self.__db_cache['lab results'] = [] 2258 if limit is None: 2259 lim = '' 2260 else: 2261 # only use limit if all other constraints are None 2262 if since is None and until is None and encounters is None and episodes is None and issues is None: 2263 lim = "limit %s" % limit 2264 else: 2265 lim = '' 2266 2267 cmd = """SELECT * FROM clin.v_results4lab_req WHERE pk_patient=%%s %s""" % lim 2268 rows, idx = gmPG.run_ro_query('historica', cmd, True, self.pk_patient) 2269 if rows is None: 2270 return False 2271 for row in rows: 2272 lab_row = { 2273 'pk_field': 'pk_result', 2274 'idx': idx, 2275 'data': row 2276 } 2277 lab_result = gmPathLab.cLabResult(row=lab_row) 2278 self.__db_cache['lab results'].append(lab_result) 2279 2280 # ok, let's constrain our list 2281 filtered_lab_results = [] 2282 filtered_lab_results.extend(self.__db_cache['lab results']) 2283 if since is not None: 2284 filtered_lab_results = filter(lambda lres: lres['req_when'] >= since, filtered_lab_results) 2285 if until is not None: 2286 filtered_lab_results = filter(lambda lres: lres['req_when'] < until, filtered_lab_results) 2287 if issues is not None: 2288 filtered_lab_results = filter(lambda lres: lres['pk_health_issue'] in issues, filtered_lab_results) 2289 if episodes is not None: 2290 filtered_lab_results = filter(lambda lres: lres['pk_episode'] in episodes, filtered_lab_results) 2291 if encounters is not None: 2292 filtered_lab_results = filter(lambda lres: lres['pk_encounter'] in encounters, filtered_lab_results) 2293 return filtered_lab_results
2294 #------------------------------------------------------------------
2295 - def get_lab_request(self, pk=None, req_id=None, lab=None):
2296 # FIXME: verify that it is our patient ? ... 2297 req = gmPathLab.cLabRequest(aPK_obj=pk, req_id=req_id, lab=lab) 2298 return req
2299 #------------------------------------------------------------------
2300 - def add_lab_request(self, lab=None, req_id=None, encounter_id=None, episode_id=None):
2301 if encounter_id is None: 2302 encounter_id = self.current_encounter['pk_encounter'] 2303 status, data = gmPathLab.create_lab_request( 2304 lab=lab, 2305 req_id=req_id, 2306 pat_id=self.pk_patient, 2307 encounter_id=encounter_id, 2308 episode_id=episode_id 2309 ) 2310 if not status: 2311 _log.error(str(data)) 2312 return None 2313 return data
2314 #============================================================ 2315 # main 2316 #------------------------------------------------------------ 2317 if __name__ == "__main__": 2318 2319 if len(sys.argv) == 1: 2320 sys.exit() 2321 2322 if sys.argv[1] != 'test': 2323 sys.exit() 2324 2325 from Gnumed.pycommon import gmLog2 2326 #-----------------------------------------
2327 - def test_allergy_state():
2328 emr = cClinicalRecord(aPKey=1) 2329 state = emr.allergy_state 2330 print "allergy state is:", state 2331 2332 print "setting state to 0" 2333 emr.allergy_state = 0 2334 2335 print "setting state to None" 2336 emr.allergy_state = None 2337 2338 print "setting state to 'abc'" 2339 emr.allergy_state = 'abc'
2340 #-----------------------------------------
2341 - def test_get_test_names():
2342 emr = cClinicalRecord(aPKey=12) 2343 rows = emr.get_test_types_for_results() 2344 print "test result names:" 2345 for row in rows: 2346 print row
2347 #-----------------------------------------
2348 - def test_get_dates_for_results():
2349 emr = cClinicalRecord(aPKey=12) 2350 rows = emr.get_dates_for_results() 2351 print "test result dates:" 2352 for row in rows: 2353 print row
2354 #-----------------------------------------
2355 - def test_get_measurements():
2356 emr = cClinicalRecord(aPKey=12) 2357 rows, idx = emr.get_measurements_by_date() 2358 print "test results:" 2359 for row in rows: 2360 print row
2361 #-----------------------------------------
2362 - def test_get_test_results_by_date():
2363 emr = cClinicalRecord(aPKey=12) 2364 tests = emr.get_test_results_by_date() 2365 print "test results:" 2366 for test in tests: 2367 print test
2368 #-----------------------------------------
2369 - def test_get_test_types_details():
2370 emr = cClinicalRecord(aPKey=12) 2371 rows, idx = emr.get_test_types_details() 2372 print "test type details:" 2373 for row in rows: 2374 print row
2375 #-----------------------------------------
2376 - def test_get_statistics():
2377 emr = cClinicalRecord(aPKey=12) 2378 for key, item in emr.get_statistics().iteritems(): 2379 print key, ":", item
2380 #-----------------------------------------
2381 - def test_get_problems():
2382 emr = cClinicalRecord(aPKey=12) 2383 2384 probs = emr.get_problems() 2385 print "normal probs (%s):" % len(probs) 2386 for p in probs: 2387 print u'%s (%s)' % (p['problem'], p['type']) 2388 2389 probs = emr.get_problems(include_closed_episodes=True) 2390 print "probs + closed episodes (%s):" % len(probs) 2391 for p in probs: 2392 print u'%s (%s)' % (p['problem'], p['type']) 2393 2394 probs = emr.get_problems(include_irrelevant_issues=True) 2395 print "probs + issues (%s):" % len(probs) 2396 for p in probs: 2397 print u'%s (%s)' % (p['problem'], p['type']) 2398 2399 probs = emr.get_problems(include_closed_episodes=True, include_irrelevant_issues=True) 2400 print "probs + issues + epis (%s):" % len(probs) 2401 for p in probs: 2402 print u'%s (%s)' % (p['problem'], p['type'])
2403 #-----------------------------------------
2404 - def test_add_test_result():
2405 emr = cClinicalRecord(aPKey=12) 2406 tr = emr.add_test_result ( 2407 episode = 1, 2408 intended_reviewer = 1, 2409 type = 1, 2410 val_num = 75, 2411 val_alpha = u'somewhat obese', 2412 unit = u'kg' 2413 ) 2414 print tr
2415 #-----------------------------------------
2416 - def test_get_most_recent_episode():
2417 emr = cClinicalRecord(aPKey=12) 2418 print emr.get_most_recent_episode(issue = 2)
2419 #-----------------------------------------
2420 - def test_get_almost_recent_encounter():
2421 emr = cClinicalRecord(aPKey=12) 2422 print emr.get_last_encounter(issue_id=2) 2423 print emr.get_last_but_one_encounter(issue_id=2)
2424 #-----------------------------------------
2425 - def test_get_meds():
2426 emr = cClinicalRecord(aPKey=12) 2427 for med in emr.get_current_substance_intake(): 2428 print med
2429 #-----------------------------------------
2430 - def test_is_allergic_to():
2431 emr = cClinicalRecord(aPKey = 12) 2432 print emr.is_allergic_to(atcs = tuple(sys.argv[2:]), inns = tuple(sys.argv[2:]), brand = sys.argv[2])
2433 #-----------------------------------------
2434 - def test_get_as_journal():
2435 emr = cClinicalRecord(aPKey = 12) 2436 for journal_line in emr.get_as_journal(): 2437 #print journal_line.keys() 2438 print u'%(date)s %(modified_by)s %(soap_cat)s %(narrative)s' % journal_line 2439 print ""
2440 #-----------------------------------------
2441 - def test_get_most_recent():
2442 emr = cClinicalRecord(aPKey=12) 2443 print emr.get_most_recent_results()
2444 #-----------------------------------------
2445 - def test_episodes():
2446 emr = cClinicalRecord(aPKey=12) 2447 print "episodes:", emr.episodes 2448 print "unlinked:", emr.unlinked_episodes
2449 #----------------------------------------- 2450 #test_allergy_state() 2451 #test_is_allergic_to() 2452 2453 #test_get_test_names() 2454 #test_get_dates_for_results() 2455 #test_get_measurements() 2456 #test_get_test_results_by_date() 2457 #test_get_test_types_details() 2458 #test_get_statistics() 2459 #test_get_problems() 2460 #test_add_test_result() 2461 #test_get_most_recent_episode() 2462 #test_get_almost_recent_encounter() 2463 #test_get_meds() 2464 #test_get_as_journal() 2465 #test_get_most_recent() 2466 test_episodes() 2467 2468 # emr = cClinicalRecord(aPKey = 12) 2469 2470 # # Vacc regimes 2471 # vacc_regimes = emr.get_scheduled_vaccination_regimes(indications = ['tetanus']) 2472 # print '\nVaccination regimes: ' 2473 # for a_regime in vacc_regimes: 2474 # pass 2475 # #print a_regime 2476 # vacc_regime = emr.get_scheduled_vaccination_regimes(ID=10) 2477 # #print vacc_regime 2478 2479 # # vaccination regimes and vaccinations for regimes 2480 # scheduled_vaccs = emr.get_scheduled_vaccinations(indications = ['tetanus']) 2481 # print 'Vaccinations for the regime:' 2482 # for a_scheduled_vacc in scheduled_vaccs: 2483 # pass 2484 # #print ' %s' %(a_scheduled_vacc) 2485 2486 # # vaccination next shot and booster 2487 # vaccinations = emr.get_vaccinations() 2488 # for a_vacc in vaccinations: 2489 # print '\nVaccination %s , date: %s, booster: %s, seq no: %s' %(a_vacc['batch_no'], a_vacc['date'].strftime('%Y-%m-%d'), a_vacc['is_booster'], a_vacc['seq_no']) 2490 2491 # # first and last encounters 2492 # first_encounter = emr.get_first_encounter(issue_id = 1) 2493 # print '\nFirst encounter: ' + str(first_encounter) 2494 # last_encounter = emr.get_last_encounter(episode_id = 1) 2495 # print '\nLast encounter: ' + str(last_encounter) 2496 # print '' 2497 2498 # # lab results 2499 # lab = emr.get_lab_results() 2500 # lab_file = open('lab-data.txt', 'wb') 2501 # for lab_result in lab: 2502 # lab_file.write(str(lab_result)) 2503 # lab_file.write('\n') 2504 # lab_file.close() 2505 2506 #dump = record.get_missing_vaccinations() 2507 #f = open('vaccs.lst', 'wb') 2508 #if dump is not None: 2509 # print "=== due ===" 2510 # f.write("=== due ===\n") 2511 # for row in dump['due']: 2512 # print row 2513 # f.write(repr(row)) 2514 # f.write('\n') 2515 # print "=== overdue ===" 2516 # f.write("=== overdue ===\n") 2517 # for row in dump['overdue']: 2518 # print row 2519 # f.write(repr(row)) 2520 # f.write('\n') 2521 #f.close() 2522