Package Gnumed :: Package exporters :: Module gmPatientExporter
[frames] | no frames]

Source Code for Module Gnumed.exporters.gmPatientExporter

   1  """GNUmed simple ASCII EMR export tool. 
   2   
   3  TODO: 
   4  - GUI mode: 
   5    - post-0.1 ! 
   6    - allow user to select patient 
   7    - allow user to pick episodes/encounters/etc from list 
   8  - output modes: 
   9    - HTML - post-0.1 ! 
  10  """ 
  11  #============================================================ 
  12  __author__ = "Carlos Moro" 
  13  __license__ = 'GPL' 
  14   
  15  import os.path, sys, types, time, codecs, datetime as pyDT, logging, shutil 
  16   
  17   
  18  import mx.DateTime.Parser as mxParser 
  19  import mx.DateTime as mxDT 
  20   
  21   
  22  if __name__ == '__main__': 
  23          sys.path.insert(0, '../../') 
  24   
  25  from Gnumed.pycommon import gmI18N 
  26   
  27  if __name__ == '__main__': 
  28          gmI18N.activate_locale() 
  29          gmI18N.install_domain() 
  30   
  31  from Gnumed.pycommon import gmExceptions, gmNull, gmPG2, gmTools, gmDateTime 
  32  from Gnumed.business import gmClinicalRecord, gmPerson, gmAllergy, gmDemographicRecord, gmClinNarrative, gmPersonSearch 
  33   
  34   
  35  _log = logging.getLogger('gm.export') 
  36  #============================================================ 
37 -class cEmrExport:
38 39 #--------------------------------------------------------
40 - def __init__(self, constraints = None, fileout = None, patient = None):
41 """ 42 Constructs a new instance of exporter 43 44 constraints - Exporter constraints for filtering clinical items 45 fileout - File-like object as target for dumping operations 46 """ 47 if constraints is None: 48 # default constraints to None for complete emr dump 49 self.__constraints = { 50 'since': None, 51 'until': None, 52 'encounters': None, 53 'episodes': None, 54 'issues': None 55 } 56 else: 57 self.__constraints = constraints 58 self.__target = fileout 59 self.__patient = patient 60 self.lab_new_encounter = True 61 self.__filtered_items = []
62 #--------------------------------------------------------
63 - def set_constraints(self, constraints = None):
64 """Sets exporter constraints. 65 66 constraints - Exporter constraints for filtering clinical items 67 """ 68 if constraints is None: 69 # default constraints to None for complete emr dump 70 self.__constraints = { 71 'since': None, 72 'until': None, 73 'encounters': None, 74 'episodes': None, 75 'issues': None 76 } 77 else: 78 self.__constraints = constraints 79 return True
80 #--------------------------------------------------------
81 - def get_constraints(self):
82 """ 83 Retrieve exporter constraints 84 """ 85 return self.__constraints
86 #--------------------------------------------------------
87 - def set_patient(self, patient=None):
88 """ 89 Sets exporter patient 90 91 patient - Patient whose data are to be dumped 92 """ 93 if patient is None: 94 _log.error("can't set None patient for exporter") 95 return 96 self.__patient = patient
97 #--------------------------------------------------------
98 - def set_output_file(self, target=None):
99 """ 100 Sets exporter output file 101 102 @param file_name - The file to dump the EMR to 103 @type file_name - FileType 104 """ 105 self.__target = target
106 #--------------------------------------------------------
107 - def get_patient(self):
108 """ 109 Retrieves patient whose data are to be dumped 110 """ 111 return self.__patient
112 #--------------------------------------------------------
113 - def cleanup(self):
114 """ 115 Exporter class cleanup code 116 """ 117 pass
118 #--------------------------------------------------------
119 - def __dump_vacc_table(self, vacc_regimes):
120 """ 121 Retrieves string containg ASCII vaccination table 122 """ 123 emr = self.__patient.get_emr() 124 # patient dob 125 patient_dob = self.__patient['dob'] 126 date_length = len(gmDateTime.pydt_strftime(patient_dob, '%Y %b %d')) + 2 127 128 # dictionary of pairs indication : scheduled vaccination 129 vaccinations4regimes = {} 130 for a_vacc_regime in vacc_regimes: 131 indication = a_vacc_regime['indication'] 132 vaccinations4regimes[indication] = emr.get_scheduled_vaccinations(indications=[indication]) 133 # vaccination regimes count 134 chart_columns = len(vacc_regimes) 135 # foot headers 136 foot_headers = ['last booster', 'next booster'] 137 # string for both: ending of vaccinations; non boosters needed 138 ending_str = '=' 139 140 # chart row count, columns width and vaccination dictionary of pairs indication : given shot 141 column_widths = [] 142 chart_rows = -1 143 vaccinations = {} 144 temp = -1 145 for foot_header in foot_headers: # first column width 146 if len(foot_header) > temp: 147 temp = len(foot_header) 148 column_widths.append(temp) 149 for a_vacc_regime in vacc_regimes: 150 if a_vacc_regime['shots'] > chart_rows: # max_seq -> row count 151 chart_rows = a_vacc_regime['shots'] 152 if (len(a_vacc_regime['l10n_indication'])) > date_length: # l10n indication -> column width 153 column_widths.append(len(a_vacc_regime['l10n_indication'])) 154 else: 155 column_widths.append(date_length) # date -> column width at least 156 vaccinations[a_vacc_regime['indication']] = emr.get_vaccinations(indications=[a_vacc_regime['indication']]) # given shots 4 indication 157 158 # patient dob in top of vaccination chart 159 txt = '\nDOB: %s' % (gmDateTime.pydt_strftime(patient_dob, '%Y %b %d')) + '\n' 160 161 # vacc chart table headers 162 # top ---- header line 163 for column_width in column_widths: 164 txt += column_width * '-' + '-' 165 txt += '\n' 166 # indication names header line 167 txt += column_widths[0] * ' ' + '|' 168 col_index = 1 169 for a_vacc_regime in vacc_regimes: 170 txt += a_vacc_regime['l10n_indication'] + (column_widths[col_index] - len(a_vacc_regime['l10n_indication'])) * ' ' + '|' 171 col_index += 1 172 txt += '\n' 173 # bottom ---- header line 174 for column_width in column_widths: 175 txt += column_width * '-' + '-' 176 txt += '\n' 177 178 # vacc chart data 179 due_date = None 180 # previously displayed date list 181 prev_displayed_date = [patient_dob] 182 for a_regime in vacc_regimes: 183 prev_displayed_date.append(patient_dob) # initialice with patient dob (useful for due first shot date calculation) 184 # iterate data rows 185 for row_index in range(0, chart_rows): 186 row_header = '#%s' %(row_index+1) 187 txt += row_header + (column_widths[0] - len(row_header)) * ' ' + '|' 188 189 for col_index in range(1, chart_columns+1): 190 indication =vacc_regimes[col_index-1]['indication'] 191 seq_no = vacc_regimes[col_index-1]['shots'] 192 if row_index == seq_no: # had just ended scheduled vaccinations 193 txt += ending_str * column_widths[col_index] + '|' 194 elif row_index < seq_no: # vaccination scheduled 195 try: 196 vacc_date = vaccinations[indication][row_index]['date'] # vaccination given 197 vacc_date_str = gmDateTime.pydt_strftime(vacc_date, '%Y %b %d') 198 txt += vacc_date_str + (column_widths[col_index] - len(vacc_date_str)) * ' ' + '|' 199 prev_displayed_date[col_index] = vacc_date 200 except: 201 if row_index == 0: # due first shot 202 due_date = prev_displayed_date[col_index] + vaccinations4regimes[indication][row_index]['age_due_min'] # FIXME 'age_due_min' not properly retrieved 203 else: # due any other than first shot 204 due_date = prev_displayed_date[col_index] + vaccinations4regimes[indication][row_index]['min_interval'] 205 txt += '('+ due_date.strftime('%Y-%m-%d') + ')' + (column_widths[col_index] - date_length) * ' ' + '|' 206 prev_displayed_date[col_index] = due_date 207 else: # not scheduled vaccination at that position 208 txt += column_widths[col_index] * ' ' + '|' 209 txt += '\n' # end of scheduled vaccination dates display 210 for column_width in column_widths: # ------ separator line 211 txt += column_width * '-' + '-' 212 txt += '\n' 213 214 # scheduled vaccination boosters (date retrieving) 215 all_vreg_boosters = [] 216 for a_vacc_regime in vacc_regimes: 217 vaccs4indication = vaccinations[a_vacc_regime['indication']] # iterate over vaccinations by indication 218 given_boosters = [] # will contain given boosters for current indication 219 for a_vacc in vaccs4indication: 220 try: 221 if a_vacc['is_booster']: 222 given_boosters.append(a_vacc) 223 except: 224 # not a booster 225 pass 226 if len(given_boosters) > 0: 227 all_vreg_boosters.append(given_boosters[len(given_boosters)-1]) # last of given boosters 228 else: 229 all_vreg_boosters.append(None) 230 231 # next booster in schedule 232 all_next_boosters = [] 233 for a_booster in all_vreg_boosters: 234 all_next_boosters.append(None) 235 # scheduled vaccination boosters (displaying string) 236 cont = 0 237 for a_vacc_regime in vacc_regimes: 238 vaccs = vaccinations4regimes[a_vacc_regime['indication']] 239 if vaccs[len(vaccs)-1]['is_booster'] == False: # booster is not scheduled/needed 240 all_vreg_boosters[cont] = ending_str * column_widths[cont+1] 241 all_next_boosters[cont] = ending_str * column_widths[cont+1] 242 else: 243 indication = vacc_regimes[cont]['indication'] 244 if len(vaccinations[indication]) > vacc_regimes[cont]['shots']: # boosters given 245 all_vreg_boosters[cont] = vaccinations[indication][len(vaccinations[indication])-1]['date'].strftime('%Y-%m-%d') # show last given booster date 246 scheduled_booster = vaccinations4regimes[indication][len(vaccinations4regimes[indication])-1] 247 booster_date = vaccinations[indication][len(vaccinations[indication])-1]['date'] + scheduled_booster['min_interval'] 248 if booster_date < mxDT.today(): 249 all_next_boosters[cont] = '<(' + booster_date.strftime('%Y-%m-%d') + ')>' # next booster is due 250 else: 251 all_next_boosters[cont] = booster_date.strftime('%Y-%m-%d') 252 elif len(vaccinations[indication]) == vacc_regimes[cont]['shots']: # just finished vaccinations, begin boosters 253 all_vreg_boosters[cont] = column_widths[cont+1] * ' ' 254 scheduled_booster = vaccinations4regimes[indication][len(vaccinations4regimes[indication])-1] 255 booster_date = vaccinations[indication][len(vaccinations[indication])-1]['date'] + scheduled_booster['min_interval'] 256 if booster_date < mxDT.today(): 257 all_next_boosters[cont] = '<(' + booster_date.strftime('%Y-%m-%d') + ')>' # next booster is due 258 else: 259 all_next_boosters[cont] = booster_date.strftime('%Y-%m-%d') 260 else: 261 all_vreg_boosters[cont] = column_widths[cont+1] * ' ' # unfinished schedule 262 all_next_boosters[cont] = column_widths[cont+1] * ' ' 263 cont += 1 264 265 # given boosters 266 foot_header = foot_headers[0] 267 col_index = 0 268 txt += foot_header + (column_widths[0] - len(foot_header)) * ' ' + '|' 269 col_index += 1 270 for a_vacc_regime in vacc_regimes: 271 txt += str(all_vreg_boosters[col_index-1]) + (column_widths[col_index] - len(str(all_vreg_boosters[col_index-1]))) * ' ' + '|' 272 col_index += 1 273 txt += '\n' 274 for column_width in column_widths: 275 txt += column_width * '-' + '-' 276 txt += '\n' 277 278 # next booster 279 foot_header = foot_headers[1] 280 col_index = 0 281 txt += foot_header + (column_widths[0] - len(foot_header)) * ' ' + '|' 282 col_index += 1 283 for a_vacc_regime in vacc_regimes: 284 txt += str(all_next_boosters[col_index-1]) + (column_widths[col_index] - len(str(all_next_boosters[col_index-1]))) * ' ' + '|' 285 col_index += 1 286 txt += '\n' 287 for column_width in column_widths: 288 txt += column_width * '-' + '-' 289 txt += '\n' 290 291 self.__target.write(txt)
292 #--------------------------------------------------------
293 - def get_vacc_table(self):
294 """ 295 Iterate over patient scheduled regimes preparing vacc tables dump 296 """ 297 298 emr = self.__patient.get_emr() 299 300 # vaccination regimes 301 all_vacc_regimes = emr.get_scheduled_vaccination_regimes() 302 # Configurable: vacc regimes per displayed table 303 # FIXME: option, post 0.1 ? 304 max_regs_per_table = 4 305 306 # Iterate over patient scheduled regimes dumping in tables of 307 # max_regs_per_table regimes per table 308 reg_count = 0 309 vacc_regimes = [] 310 for total_reg_count in range(0,len(all_vacc_regimes)): 311 if reg_count%max_regs_per_table == 0: 312 if len(vacc_regimes) > 0: 313 self.__dump_vacc_table(vacc_regimes) 314 vacc_regimes = [] 315 reg_count = 0 316 vacc_regimes.append(all_vacc_regimes[total_reg_count]) 317 reg_count += 1 318 if len(vacc_regimes) > 0: 319 self.__dump_vacc_table(vacc_regimes)
320 321 #--------------------------------------------------------
322 - def dump_item_fields(self, offset, item, field_list):
323 """ 324 Dump information related to the fields of a clinical item 325 offset - Number of left blank spaces 326 item - Item of the field to dump 327 fields - Fields to dump 328 """ 329 txt = '' 330 for a_field in field_list: 331 if type(a_field) is not types.UnicodeType: 332 a_field = unicode(a_field, encoding='latin1', errors='replace') 333 txt += u'%s%s%s' % ((offset * u' '), a_field, gmTools.coalesce(item[a_field], u'\n', template_initial = u': %s\n')) 334 return txt
335 #--------------------------------------------------------
336 - def get_allergy_output(self, allergy, left_margin = 0):
337 """ 338 Dumps allergy item data 339 allergy - Allergy item to dump 340 left_margin - Number of spaces on the left margin 341 """ 342 txt = '' 343 txt += left_margin*' ' + _('Allergy') + ': \n' 344 txt += self.dump_item_fields((left_margin+3), allergy, ['allergene', 'substance', 'generic_specific','l10n_type', 'definite', 'reaction']) 345 return txt
346 #--------------------------------------------------------
347 - def get_vaccination_output(self, vaccination, left_margin = 0):
348 """ 349 Dumps vaccination item data 350 vaccination - Vaccination item to dump 351 left_margin - Number of spaces on the left margin 352 """ 353 txt = '' 354 txt += left_margin*' ' + _('Vaccination') + ': \n' 355 txt += self.dump_item_fields((left_margin+3), vaccination, ['l10n_indication', 'vaccine', 'batch_no', 'site', 'narrative']) 356 return txt
357 #--------------------------------------------------------
358 - def get_lab_result_output(self, lab_result, left_margin = 0):
359 """ 360 Dumps lab result item data 361 lab_request - Lab request item to dump 362 left_margin - Number of spaces on the left margin 363 """ 364 txt = '' 365 if self.lab_new_encounter: 366 txt += (left_margin)*' ' + _('Lab result') + ': \n' 367 txt += (left_margin+3) * ' ' + lab_result['unified_name'] + ': ' + lab_result['unified_val']+ ' ' + lab_result['val_unit'] + ' (' + lab_result['material'] + ')' + '\n' 368 return txt
369 #--------------------------------------------------------
370 - def get_item_output(self, item, left_margin = 0):
371 """ 372 Obtains formatted clinical item output dump 373 item - The clinical item to dump 374 left_margin - Number of spaces on the left margin 375 """ 376 txt = '' 377 if isinstance(item, gmAllergy.cAllergy): 378 txt += self.get_allergy_output(item, left_margin) 379 # elif isinstance(item, gmVaccination.cVaccination): 380 # txt += self.get_vaccination_output(item, left_margin) 381 # elif isinstance(item, gmPathLab.cLabResult): 382 # txt += self.get_lab_result_output(item, left_margin) 383 # self.lab_new_encounter = False 384 return txt
385 #--------------------------------------------------------
386 - def __fetch_filtered_items(self):
387 """ 388 Retrieve patient clinical items filtered by multiple constraints 389 """ 390 if not self.__patient.connected: 391 return False 392 emr = self.__patient.get_emr() 393 filtered_items = [] 394 filtered_items.extend(emr.get_allergies( 395 since=self.__constraints['since'], 396 until=self.__constraints['until'], 397 encounters=self.__constraints['encounters'], 398 episodes=self.__constraints['episodes'], 399 issues=self.__constraints['issues']) 400 ) 401 self.__filtered_items = filtered_items 402 return True
403 #--------------------------------------------------------
404 - def get_allergy_summary(self, allergy, left_margin = 0):
405 """ 406 Dumps allergy item data summary 407 allergy - Allergy item to dump 408 left_margin - Number of spaces on the left margin 409 """ 410 txt = _('%sAllergy: %s, %s (noted %s)\n') % ( 411 left_margin * u' ', 412 allergy['descriptor'], 413 gmTools.coalesce(allergy['reaction'], _('unknown reaction')), 414 gmDateTime.pydt_strftime(allergy['date'], '%Y %b %d') 415 ) 416 # txt = left_margin * ' ' \ 417 # + _('Allergy') + ': ' \ 418 # + allergy['descriptor'] + u', ' \ 419 # + gmTools.coalesce(allergy['reaction'], _('unknown reaction')) ' ' \ 420 # + _('(noted %s)') % gmDateTime.pydt_strftime(allergy['date'], '%Y %b %d') \ 421 # + '\n' 422 return txt
423 #--------------------------------------------------------
424 - def get_vaccination_summary(self, vaccination, left_margin = 0):
425 """ 426 Dumps vaccination item data summary 427 vaccination - Vaccination item to dump 428 left_margin - Number of spaces on the left margin 429 """ 430 txt = left_margin*' ' + _('Vaccination') + ': ' + vaccination['l10n_indication'] + ', ' + \ 431 vaccination['narrative'] + '\n' 432 return txt
433 #--------------------------------------------------------
434 - def get_lab_result_summary(self, lab_result, left_margin = 0):
435 """ 436 Dumps lab result item data summary 437 lab_request - Lab request item to dump 438 left_margin - Number of spaces on the left margin 439 """ 440 txt = '' 441 if self.lab_new_encounter: 442 txt += (left_margin+3)*' ' + _('Lab') + ': ' + \ 443 lab_result['unified_name'] + '-> ' + lab_result['unified_val'] + \ 444 ' ' + lab_result['val_unit']+ '\n' + '(' + lab_result['req_when'].strftime('%Y-%m-%d') + ')' 445 return txt
446 #--------------------------------------------------------
447 - def get_item_summary(self, item, left_margin = 0):
448 """ 449 Obtains formatted clinical item summary dump 450 item - The clinical item to dump 451 left_margin - Number of spaces on the left margin 452 """ 453 txt = '' 454 if isinstance(item, gmAllergy.cAllergy): 455 txt += self.get_allergy_summary(item, left_margin) 456 # elif isinstance(item, gmVaccination.cVaccination): 457 # txt += self.get_vaccination_summary(item, left_margin) 458 # elif isinstance(item, gmPathLab.cLabResult) and \ 459 # True: 460 # #(item['relevant'] == True or item['abnormal'] == True): 461 # txt += self.get_lab_result_summary(item, left_margin) 462 # self.lab_new_encounter = False 463 return txt
464 #--------------------------------------------------------
465 - def refresh_historical_tree(self, emr_tree):
466 """ 467 checks a emr_tree constructed with this.get_historical_tree() 468 and sees if any new items need to be inserted. 469 """ 470 #TODO , caching eliminates tree update time, so don't really need this 471 self._traverse_health_issues( emr_tree, self._update_health_issue_branch)
472 #--------------------------------------------------------
473 - def get_historical_tree( self, emr_tree):
474 self._traverse_health_issues( emr_tree, self._add_health_issue_branch)
475 #--------------------------------------------------------
476 - def _traverse_health_issues(self, emr_tree, health_issue_action):
477 """ 478 Retrieves patient's historical in form of a wx tree of health issues 479 -> episodes 480 -> encounters 481 Encounter object is associated with item to allow displaying its information 482 """ 483 # variable initialization 484 # this protects the emrBrowser from locking up in a paint event, e.g. in 485 # some configurations which want to use emrBrowser, but don't stop tabbing 486 # to emr browser when no patient selected. the effect is to displace a cNull instance 487 # which is a sane representation when no patient is selected. 488 if not self.__fetch_filtered_items(): 489 return 490 emr = self.__patient.get_emr() 491 unlinked_episodes = emr.get_episodes(issues = [None]) 492 h_issues = [] 493 h_issues.extend(emr.get_health_issues(id_list = self.__constraints['issues'])) 494 # build the tree 495 # unlinked episodes 496 if len(unlinked_episodes) > 0: 497 h_issues.insert(0, { 498 'description': _('Unattributed episodes'), 499 'pk_health_issue': None 500 }) 501 # existing issues 502 for a_health_issue in h_issues: 503 health_issue_action( emr_tree, a_health_issue) 504 505 root_item = emr_tree.GetRootItem() 506 if len(h_issues) == 0: 507 emr_tree.SetItemHasChildren(root_item, False) 508 else: 509 emr_tree.SetItemHasChildren(root_item, True) 510 emr_tree.SortChildren(root_item)
511 #--------------------------------------------------------
512 - def _add_health_issue_branch( self, emr_tree, a_health_issue):
513 """appends to a wx emr_tree , building wx treenodes from the health_issue make this reusable for non-collapsing tree updates""" 514 emr = self.__patient.get_emr() 515 root_node = emr_tree.GetRootItem() 516 issue_node = emr_tree.AppendItem(root_node, a_health_issue['description']) 517 emr_tree.SetItemPyData(issue_node, a_health_issue) 518 episodes = emr.get_episodes(id_list=self.__constraints['episodes'], issues = [a_health_issue['pk_health_issue']]) 519 if len(episodes) == 0: 520 emr_tree.SetItemHasChildren(issue_node, False) 521 else: 522 emr_tree.SetItemHasChildren(issue_node, True) 523 for an_episode in episodes: 524 self._add_episode_to_tree( emr, emr_tree, issue_node,a_health_issue, an_episode) 525 emr_tree.SortChildren(issue_node)
526 #--------------------------------------------------------
527 - def _add_episode_to_tree( self, emr , emr_tree, issue_node, a_health_issue, an_episode):
528 episode_node = emr_tree.AppendItem(issue_node, an_episode['description']) 529 emr_tree.SetItemPyData(episode_node, an_episode) 530 if an_episode['episode_open']: 531 emr_tree.SetItemBold(issue_node, True) 532 533 encounters = self._get_encounters( an_episode, emr ) 534 if len(encounters) == 0: 535 emr_tree.SetItemHasChildren(episode_node, False) 536 else: 537 emr_tree.SetItemHasChildren(episode_node, True) 538 self._add_encounters_to_tree( encounters, emr_tree, episode_node ) 539 emr_tree.SortChildren(episode_node) 540 return episode_node
541 #--------------------------------------------------------
542 - def _add_encounters_to_tree( self, encounters, emr_tree, episode_node):
543 for an_encounter in encounters: 544 # label = u'%s: %s' % (an_encounter['started'].strftime('%Y-%m-%d'), an_encounter['l10n_type']) 545 label = u'%s: %s' % ( 546 an_encounter['started'].strftime('%Y-%m-%d'), 547 gmTools.unwrap ( 548 gmTools.coalesce ( 549 gmTools.coalesce ( 550 gmTools.coalesce ( 551 an_encounter.get_latest_soap ( # soAp 552 soap_cat = 'a', 553 episode = emr_tree.GetPyData(episode_node)['pk_episode'] 554 ), 555 an_encounter['assessment_of_encounter'] # or AOE 556 ), 557 an_encounter['reason_for_encounter'] # or RFE 558 ), 559 an_encounter['l10n_type'] # or type 560 ), 561 max_length = 40 562 ) 563 ) 564 encounter_node_id = emr_tree.AppendItem(episode_node, label) 565 emr_tree.SetItemPyData(encounter_node_id, an_encounter) 566 emr_tree.SetItemHasChildren(encounter_node_id, False)
567 #--------------------------------------------------------
568 - def _get_encounters ( self, an_episode, emr ):
569 encounters = emr.get_encounters ( 570 episodes = [an_episode['pk_episode']] 571 ) 572 return encounters
573 #--------------------------------------------------------
574 - def _update_health_issue_branch(self, emr_tree, a_health_issue):
575 emr = self.__patient.get_emr() 576 root_node = emr_tree.GetRootItem() 577 id, cookie = emr_tree.GetFirstChild(root_node) 578 found = False 579 while id.IsOk(): 580 if emr_tree.GetItemText(id) == a_health_issue['description']: 581 found = True 582 break 583 id,cookie = emr_tree.GetNextChild( root_node, cookie) 584 585 if not found: 586 _log.error("health issue %s should exist in tree already", a_health_issue['description'] ) 587 return 588 issue_node = id 589 episodes = emr.get_episodes(id_list=self.__constraints['episodes'], issues = [a_health_issue['pk_health_issue']]) 590 591 #check for removed episode and update tree 592 tree_episodes = {} 593 id_episode, cookie = emr_tree.GetFirstChild(issue_node) 594 while id_episode.IsOk(): 595 tree_episodes[ emr_tree.GetPyData(id_episode)['pk_episode'] ]= id_episode 596 id_episode,cookie = emr_tree.GetNextChild( issue_node, cookie) 597 598 existing_episode_pk = [ e['pk_episode'] for e in episodes] 599 missing_tree_pk = [ pk for pk in tree_episodes.keys() if pk not in existing_episode_pk] 600 for pk in missing_tree_pk: 601 emr_tree.Remove( tree_episodes[pk] ) 602 603 added_episode_pk = [pk for pk in existing_episode_pk if pk not in tree_episodes.keys()] 604 add_episodes = [ e for e in episodes if e['pk_episode'] in added_episode_pk] 605 606 #check for added episodes and update tree 607 for an_episode in add_episodes: 608 node = self._add_episode_to_tree( emr, emr_tree, issue_node, a_health_issue, an_episode) 609 tree_episodes[an_episode['pk_episode']] = node 610 611 for an_episode in episodes: 612 # found episode, check for encounter change 613 try: 614 #print "getting id_episode of ", an_episode['pk_episode'] 615 id_episode = tree_episodes[an_episode['pk_episode']] 616 except: 617 import pdb 618 pdb.set_trace() 619 # get a map of encounters in the tree by pk_encounter as key 620 tree_enc = {} 621 id_encounter, cookie = emr_tree.GetFirstChild(id_episode) 622 while id_encounter.IsOk(): 623 tree_enc[ emr_tree.GetPyData(id_encounter)['pk_encounter'] ] = id_encounter 624 id_encounter,cookie = emr_tree.GetNextChild(id_episode, cookie) 625 626 # remove encounters in tree not in existing encounters in episode 627 # encounters = self._get_encounters( a_health_issue, an_episode, emr ) 628 encounters = self._get_encounters( an_episode, emr ) 629 existing_enc_pk = [ enc['pk_encounter'] for enc in encounters] 630 missing_enc_pk = [ pk for pk in tree_enc.keys() if pk not in existing_enc_pk] 631 for pk in missing_enc_pk: 632 emr_tree.Remove( tree_enc[pk] ) 633 634 # check for added encounter 635 added_enc_pk = [ pk for pk in existing_enc_pk if pk not in tree_enc.keys() ] 636 add_encounters = [ enc for enc in encounters if enc['pk_encounter'] in added_enc_pk] 637 if add_encounters != []: 638 #print "DEBUG found encounters to add" 639 self._add_encounters_to_tree( add_encounters, emr_tree, id_episode)
640 #--------------------------------------------------------
641 - def get_summary_info(self, left_margin = 0):
642 """ 643 Dumps patient EMR summary 644 """ 645 txt = '' 646 for an_item in self.__filtered_items: 647 txt += self.get_item_summary(an_item, left_margin) 648 return txt
649 #--------------------------------------------------------
650 - def get_episode_summary (self, episode, left_margin = 0):
651 """Dumps episode specific data""" 652 emr = self.__patient.get_emr() 653 encs = emr.get_encounters(episodes = [episode['pk_episode']]) 654 if encs is None: 655 txt = left_margin * ' ' + _('Error retrieving encounters for episode\n%s') % str(episode) 656 return txt 657 no_encs = len(encs) 658 if no_encs == 0: 659 txt = left_margin * ' ' + _('There are no encounters for this episode.') 660 return txt 661 if episode['episode_open']: 662 status = _('active') 663 else: 664 status = _('finished') 665 first_encounter = emr.get_first_encounter(episode_id = episode['pk_episode']) 666 last_encounter = emr.get_last_encounter(episode_id = episode['pk_episode']) 667 txt = _( 668 '%sEpisode "%s" [%s]\n' 669 '%sEncounters: %s (%s - %s)\n' 670 '%sLast worked on: %s\n' 671 ) % ( 672 left_margin * ' ', episode['description'], status, 673 left_margin * ' ', no_encs, first_encounter['started'].strftime('%m/%Y'), last_encounter['last_affirmed'].strftime('%m/%Y'), 674 left_margin * ' ', last_encounter['last_affirmed'].strftime('%Y-%m-%d %H:%M') 675 ) 676 return txt
677 #--------------------------------------------------------
678 - def get_encounter_info(self, episode, encounter, left_margin = 0):
679 """ 680 Dumps encounter specific data (rfe, aoe and soap) 681 """ 682 emr = self.__patient.get_emr() 683 # general 684 txt = (' ' * left_margin) + '#%s: %s - %s %s' % ( 685 encounter['pk_encounter'], 686 encounter['started'].strftime('%Y-%m-%d %H:%M'), 687 encounter['last_affirmed'].strftime('%H:%M (%Z)'), 688 encounter['l10n_type'] 689 ) 690 if (encounter['assessment_of_encounter'] is not None) and (len(encounter['assessment_of_encounter']) > 0): 691 txt += ' "%s"' % encounter['assessment_of_encounter'] 692 txt += '\n\n' 693 694 # rfe/aoe 695 txt += (' ' * left_margin) + '%s: %s\n' % (_('RFE'), encounter['reason_for_encounter']) 696 txt += (' ' * left_margin) + '%s: %s\n' % (_('AOE'), encounter['assessment_of_encounter']) 697 698 # soap 699 soap_cat_labels = { 700 's': _('Subjective'), 701 'o': _('Objective'), 702 'a': _('Assessment'), 703 'p': _('Plan'), 704 None: _('Administrative') 705 } 706 eol_w_margin = '\n' + (' ' * (left_margin+3)) 707 for soap_cat in 'soapu': 708 soap_cat_narratives = emr.get_clin_narrative ( 709 episodes = [episode['pk_episode']], 710 encounters = [encounter['pk_encounter']], 711 soap_cats = [soap_cat] 712 ) 713 if soap_cat_narratives is None: 714 continue 715 if len(soap_cat_narratives) == 0: 716 continue 717 txt += (' ' * left_margin) + soap_cat_labels[soap_cat] + ':\n' 718 for soap_entry in soap_cat_narratives: 719 txt += gmTools.wrap ( 720 '%s %.8s: %s\n' % ( 721 soap_entry['date'].strftime('%d.%m. %H:%M'), 722 soap_entry['provider'], 723 soap_entry['narrative'] 724 ), 75 725 ) 726 727 # txt += ( 728 # (' ' * (left_margin+3)) + 729 # soap_entry['date'].strftime('%H:%M %.8s: ') % soap_entry['provider'] + 730 # soap_entry['narrative'].replace('\n', eol_w_margin) + 731 # '\n' 732 # ) 733 #FIXME: add diagnoses 734 735 # items 736 for an_item in self.__filtered_items: 737 if an_item['pk_encounter'] == encounter['pk_encounter']: 738 txt += self.get_item_output(an_item, left_margin) 739 return txt
740 #--------------------------------------------------------
741 - def dump_historical_tree(self):
742 """Dumps patient's historical in form of a tree of health issues 743 -> episodes 744 -> encounters 745 -> clinical items 746 """ 747 748 # fecth all values 749 self.__fetch_filtered_items() 750 emr = self.__patient.get_emr() 751 752 # dump clinically relevant items summary 753 for an_item in self.__filtered_items: 754 self.__target.write(self.get_item_summary(an_item, 3)) 755 756 # begin with the tree 757 h_issues = [] 758 h_issues.extend(emr.get_health_issues(id_list = self.__constraints['issues'])) 759 # unlinked episodes 760 unlinked_episodes = emr.get_episodes(issues = [None]) 761 if len(unlinked_episodes) > 0: 762 h_issues.insert(0, {'description':_('Unattributed episodes'), 'pk_health_issue':None}) 763 for a_health_issue in h_issues: 764 self.__target.write('\n' + 3*' ' + 'Health Issue: ' + a_health_issue['description'] + '\n') 765 episodes = emr.get_episodes(id_list=self.__constraints['episodes'], issues = [a_health_issue['pk_health_issue']]) 766 for an_episode in episodes: 767 self.__target.write('\n' + 6*' ' + 'Episode: ' + an_episode['description'] + '\n') 768 if a_health_issue['pk_health_issue'] is None: 769 issues = None 770 else: 771 issues = [a_health_issue['pk_health_issue']] 772 encounters = emr.get_encounters ( 773 since = self.__constraints['since'], 774 until = self.__constraints['until'], 775 id_list = self.__constraints['encounters'], 776 episodes = [an_episode['pk_episode']], 777 issues = issues 778 ) 779 for an_encounter in encounters: 780 # title 781 self.lab_new_encounter = True 782 self.__target.write( 783 '\n %s %s: %s - %s (%s)\n' % ( 784 _('Encounter'), 785 an_encounter['l10n_type'], 786 an_encounter['started'].strftime('%A, %Y-%m-%d %H:%M'), 787 an_encounter['last_affirmed'].strftime('%m-%d %H:%M'), 788 an_encounter['assessment_of_encounter'] 789 ) 790 ) 791 self.__target.write(self.get_encounter_info(an_episode, an_encounter, 12))
792 #--------------------------------------------------------
793 - def dump_clinical_record(self):
794 """ 795 Dumps in ASCII format patient's clinical record 796 """ 797 emr = self.__patient.get_emr() 798 if emr is None: 799 _log.error('cannot get EMR text dump') 800 print(_( 801 'An error occurred while retrieving a text\n' 802 'dump of the EMR for the active patient.\n\n' 803 'Please check the log file for details.' 804 )) 805 return None 806 self.__target.write('\nOverview\n') 807 self.__target.write('--------\n') 808 self.__target.write("1) Allergy status (for details, see below):\n\n") 809 for allergy in emr.get_allergies(): 810 self.__target.write(" " + allergy['descriptor'] + "\n\n") 811 self.__target.write("2) Vaccination status (* indicates booster):\n") 812 # self.get_vacc_table() 813 self.__target.write("\n3) Historical:\n\n") 814 self.dump_historical_tree() 815 816 try: 817 emr.cleanup() 818 except: 819 print "error cleaning up EMR"
820 #--------------------------------------------------------
821 - def dump_med_docs(self):
822 """ 823 Dumps patient stored medical documents 824 825 """ 826 doc_folder = self.__patient.get_document_folder() 827 828 self.__target.write('\n4) Medical documents: (date) reference - type "comment"\n') 829 self.__target.write(' object - comment') 830 831 docs = doc_folder.get_documents() 832 for doc in docs: 833 self.__target.write('\n\n (%s) %s - %s "%s"' % ( 834 doc['clin_when'].strftime('%Y-%m-%d'), 835 doc['ext_ref'], 836 doc['l10n_type'], 837 doc['comment']) 838 ) 839 for part in doc.parts: 840 self.__target.write('\n %s - %s' % ( 841 part['seq_idx'], 842 part['obj_comment']) 843 ) 844 self.__target.write('\n\n')
845 #--------------------------------------------------------
846 - def dump_demographic_record(self, all = False):
847 """ 848 Dumps in ASCII format some basic patient's demographic data 849 """ 850 if self.__patient is None: 851 _log.error('cannot get Demographic export') 852 print(_( 853 'An error occurred while Demographic record export\n' 854 'Please check the log file for details.' 855 )) 856 return None 857 858 self.__target.write('\n\n\nDemographics') 859 self.__target.write('\n------------\n') 860 self.__target.write(' Id: %s \n' % self.__patient['pk_identity']) 861 cont = 0 862 for name in self.__patient.get_names(): 863 if cont == 0: 864 self.__target.write(' Name (Active): %s, %s\n' % (name['firstnames'], name['lastnames']) ) 865 else: 866 self.__target.write(' Name %s: %s, %s\n' % (cont, name['firstnames'], name['lastnames'])) 867 cont += 1 868 self.__target.write(' Gender: %s\n' % self.__patient['gender']) 869 self.__target.write(' Title: %s\n' % self.__patient['title']) 870 self.__target.write(' Dob: %s\n' % self.__patient.get_formatted_dob(format = '%Y-%m-%d')) 871 self.__target.write(' Medical age: %s\n' % self.__patient.get_medical_age())
872 #--------------------------------------------------------
873 - def dump_constraints(self):
874 """ 875 Dumps exporter filtering constraints 876 """ 877 self.__first_constraint = True 878 if not self.__constraints['since'] is None: 879 self.dump_constraints_header() 880 self.__target.write('\nSince: %s' % self.__constraints['since'].strftime('%Y-%m-%d')) 881 882 if not self.__constraints['until'] is None: 883 self.dump_constraints_header() 884 self.__target.write('\nUntil: %s' % self.__constraints['until'].strftime('%Y-%m-%d')) 885 886 if not self.__constraints['encounters'] is None: 887 self.dump_constraints_header() 888 self.__target.write('\nEncounters: ') 889 for enc in self.__constraints['encounters']: 890 self.__target.write(str(enc) + ' ') 891 892 if not self.__constraints['episodes'] is None: 893 self.dump_constraints_header() 894 self.__target.write('\nEpisodes: ') 895 for epi in self.__constraints['episodes']: 896 self.__target.write(str(epi) + ' ') 897 898 if not self.__constraints['issues'] is None: 899 self.dump_constraints_header() 900 self.__target.write('\nIssues: ') 901 for iss in self.__constraints['issues']: 902 self.__target.write(str(iss) + ' ')
903 #--------------------------------------------------------
904 - def dump_constraints_header(self):
905 """ 906 Dumps constraints header 907 """ 908 if self.__first_constraint == True: 909 self.__target.write('\nClinical items dump constraints\n') 910 self.__target.write('-'*(len(head_txt)-2)) 911 self.__first_constraint = False
912 #============================================================
913 -class cEMRJournalExporter:
914 """Exports patient EMR into a simple chronological journal. 915 916 Note that this export will emit u'' strings only. 917 """
918 - def __init__(self):
919 self.__part_len = 72
920 #-------------------------------------------------------- 921 # external API 922 #--------------------------------------------------------
923 - def export_to_file_by_mod_time(self, filename=None, patient=None):
924 if patient is None: 925 patient = gmPerson.gmCurrentPatient() 926 if not patient.connected: 927 raise ValueError('[%s].export_to_file(): no active patient' % self.__class__.__name__) 928 929 if filename is None: 930 filename = gmTools.get_unique_filename(prefix = 'gm-emr_by_mod_time-', suffix = '.txt') 931 932 f = codecs.open(filename = filename, mode = 'w+b', encoding = 'utf8', errors = 'replace') 933 934 self.__part_len = 80 935 936 # write header 937 txt = _('EMR Journal sorted by last modification time\n') 938 f.write(txt) 939 f.write(u'=' * (len(txt)-1)) 940 f.write('\n') 941 f.write(_('Patient: %s (%s), No: %s\n') % (patient['description'], patient['gender'], patient['pk_identity'])) 942 f.write(_('Born : %s, age: %s\n\n') % ( 943 patient.get_formatted_dob(format = '%Y %b %d', encoding = gmI18N.get_encoding()), 944 patient.get_medical_age() 945 )) 946 947 # get data 948 cmd = u""" 949 SELECT 950 vemrj.*, 951 to_char(vemrj.modified_when, 'YYYY-MM-DD HH24:MI') AS date_modified 952 FROM clin.v_emr_journal vemrj 953 WHERE pk_patient = %(pat)s 954 ORDER BY modified_when 955 """ 956 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'pat': patient['pk_identity']}}]) 957 958 f.write ((u'-' * 100) + u'\n') 959 f.write (u'%16.16s | %9.9s | %1.1s | %s \n' % (_('Last modified'), _('By'), u' ', _('Entry'))) 960 f.write ((u'-' * 100) + u'\n') 961 962 for r in rows: 963 txt = u'%16.16s | %9.9s | %1.1s | %s \n' % ( 964 r['date_modified'], 965 r['modified_by'], 966 gmClinNarrative.soap_cat2l10n[r['soap_cat']], 967 gmTools.wrap ( 968 text = r['narrative'].replace(u'\r', u'') + u' (%s: %s)' % (_('When'), gmDateTime.pydt_strftime(r['clin_when'], '%Y %b %d %H:%M')), 969 width = self.__part_len, 970 subsequent_indent = u'%31.31s%1.1s | ' % (u' ', gmClinNarrative.soap_cat2l10n[r['soap_cat']]) 971 ) 972 ) 973 f.write(txt) 974 975 f.write((u'-' * 100) + u'\n\n') 976 f.write(_('Exported: %s\n') % gmDateTime.pydt_strftime(gmDateTime.pydt_now_here(), '%Y %b %d %H:%M:%S')) 977 978 f.close() 979 return filename
980 #--------------------------------------------------------
981 - def export_to_file(self, filename=None, patient=None):
982 """Export medical record into a file. 983 984 @type filename: None (creates filename by itself) or string 985 @type patient: None (use currently active patient) or <gmPerson.cIdentity> instance 986 """ 987 if patient is None: 988 patient = gmPerson.gmCurrentPatient() 989 if not patient.connected: 990 raise ValueError('[%s].export_to_file(): no active patient' % self.__class__.__name__) 991 992 if filename is None: 993 filename = u'%s-%s-%s-%s.txt' % ( 994 _('emr-journal'), 995 patient['lastnames'].replace(u' ', u'_'), 996 patient['firstnames'].replace(u' ', u'_'), 997 patient.get_formatted_dob(format = '%Y-%m-%d') 998 ) 999 path = os.path.expanduser(os.path.join('~', 'gnumed', patient['dirname'], filename)) 1000 1001 f = codecs.open(filename = filename, mode = 'w+b', encoding = 'utf8', errors = 'replace') 1002 self.export(target = f, patient = patient) 1003 f.close() 1004 return filename
1005 #-------------------------------------------------------- 1006 # internal API 1007 #--------------------------------------------------------
1008 - def export(self, target=None, patient=None):
1009 """ 1010 Export medical record into a Python object. 1011 1012 @type target: a python object supporting the write() API 1013 @type patient: None (use currently active patient) or <gmPerson.cIdentity> instance 1014 """ 1015 if patient is None: 1016 patient = gmPerson.gmCurrentPatient() 1017 if not patient.connected: 1018 raise ValueError('[%s].export(): no active patient' % self.__class__.__name__) 1019 1020 # write header 1021 txt = _('Chronological EMR Journal\n') 1022 target.write(txt) 1023 target.write(u'=' * (len(txt)-1)) 1024 target.write('\n') 1025 target.write(_('Patient: %s (%s), No: %s\n') % (patient['description'], patient['gender'], patient['pk_identity'])) 1026 target.write(_('Born : %s, age: %s\n\n') % ( 1027 patient.get_formatted_dob(format = '%Y %b %d', encoding = gmI18N.get_encoding()), 1028 patient.get_medical_age() 1029 )) 1030 target.write(u'.-%10.10s---%9.9s-------%72.72s\n' % (u'-' * 10, u'-' * 9, u'-' * self.__part_len)) 1031 target.write(u'| %10.10s | %9.9s | | %s\n' % (_('Encounter'), _('Doc'), _('Narrative'))) 1032 target.write(u'|-%10.10s---%9.9s-------%72.72s\n' % (u'-' * 10, u'-' * 9, u'-' * self.__part_len)) 1033 1034 # get data 1035 cmd = u""" 1036 SELECT 1037 to_char(vemrj.clin_when, 'YYYY-MM-DD') AS date, 1038 vemrj.*, 1039 (SELECT rank FROM clin.soap_cat_ranks WHERE soap_cat = vemrj.soap_cat) AS scr, 1040 to_char(vemrj.modified_when, 'YYYY-MM-DD HH24:MI') AS date_modified 1041 FROM clin.v_emr_journal vemrj 1042 WHERE pk_patient = %s 1043 ORDER BY date, pk_episode, scr, src_table 1044 """ 1045 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [patient['pk_identity']]}], get_col_idx = True) 1046 1047 # write data 1048 prev_date = u'' 1049 prev_doc = u'' 1050 prev_soap = u'' 1051 for row in rows: 1052 # narrative 1053 if row['narrative'] is None: 1054 continue 1055 1056 txt = gmTools.wrap ( 1057 text = row['narrative'].replace(u'\r', u'') + (u' (%s)' % row['date_modified']), 1058 width = self.__part_len 1059 ).split('\n') 1060 1061 # same provider ? 1062 curr_doc = row['modified_by'] 1063 if curr_doc != prev_doc: 1064 prev_doc = curr_doc 1065 else: 1066 curr_doc = u'' 1067 1068 # same soap category ? 1069 curr_soap = row['soap_cat'] 1070 if curr_soap != prev_soap: 1071 prev_soap = curr_soap 1072 1073 # same date ? 1074 curr_date = row['date'] 1075 if curr_date != prev_date: 1076 prev_date = curr_date 1077 curr_doc = row['modified_by'] 1078 prev_doc = curr_doc 1079 curr_soap = row['soap_cat'] 1080 prev_soap = curr_soap 1081 else: 1082 curr_date = u'' 1083 1084 # display first part 1085 target.write(u'| %10.10s | %9.9s | %3.3s | %s\n' % ( 1086 curr_date, 1087 curr_doc, 1088 gmClinNarrative.soap_cat2l10n[curr_soap], 1089 txt[0] 1090 )) 1091 1092 # only one part ? 1093 if len(txt) == 1: 1094 continue 1095 1096 template = u'| %10.10s | %9.9s | %3.3s | %s\n' 1097 for part in txt[1:]: 1098 line = template % (u'', u'', u' ', part) 1099 target.write(line) 1100 1101 # write footer 1102 target.write(u'`-%10.10s---%9.9s-------%72.72s\n\n' % (u'-' * 10, u'-' * 9, u'-' * self.__part_len)) 1103 target.write(_('Exported: %s\n') % gmDateTime.pydt_strftime(pyDT.datetime.now(), '%Y %b %d %H:%M:%S')) 1104 1105 return
1106 #============================================================
1107 -class cMedistarSOAPExporter:
1108 """Export SOAP data per encounter into Medistar import format."""
1109 - def __init__(self, patient=None):
1110 if patient is None: 1111 self.__pat = gmPerson.gmCurrentPatient() 1112 else: 1113 if not isinstance(patient, gmPerson.cIdentity): 1114 raise gmExceptions.ConstructorError, '<patient> argument must be instance of <cIdentity>, but is: %s' % type(patient) 1115 self.__pat = patient
1116 #-------------------------------------------------------- 1117 # external API 1118 #--------------------------------------------------------
1119 - def export_to_file(self, filename=None, encounter=None, soap_cats=u'soapu', export_to_import_file=False):
1120 if not self.__pat.connected: 1121 return (False, 'no active patient') 1122 1123 if filename is None: 1124 path = os.path.abspath(os.path.expanduser('~/gnumed/export')) 1125 filename = '%s-%s-%s-%s-%s.txt' % ( 1126 os.path.join(path, 'Medistar-MD'), 1127 time.strftime('%Y-%m-%d',time.localtime()), 1128 self.__pat['lastnames'].replace(' ', '-'), 1129 self.__pat['firstnames'].replace(' ', '_'), 1130 self.__pat.get_formatted_dob(format = '%Y-%m-%d') 1131 ) 1132 1133 f = codecs.open(filename = filename, mode = 'w+b', encoding = 'cp437', errors='replace') 1134 status = self.__export(target = f, encounter = encounter, soap_cats = soap_cats) 1135 f.close() 1136 1137 if export_to_import_file: 1138 # detect "LW:\medistar\inst\soap.txt" 1139 medistar_found = False 1140 for drive in u'cdefghijklmnopqrstuvwxyz': 1141 path = drive + ':\\medistar\\inst' 1142 if not os.path.isdir(path): 1143 continue 1144 try: 1145 import_fname = path + '\\soap.txt' 1146 open(import_fname, mode = 'w+b').close() 1147 _log.debug('exporting narrative to [%s] for Medistar import', import_fname) 1148 shutil.copyfile(filename, import_fname) 1149 medistar_found = True 1150 except IOError: 1151 continue 1152 1153 if not medistar_found: 1154 _log.debug('no Medistar installation found (no <LW>:\\medistar\\inst\\)') 1155 1156 return (status, filename)
1157 #--------------------------------------------------------
1158 - def export(self, target, encounter=None, soap_cats=u'soapu'):
1159 return self.__export(target, encounter = encounter, soap_cats = soap_cats)
1160 #-------------------------------------------------------- 1161 # interal API 1162 #--------------------------------------------------------
1163 - def __export(self, target=None, encounter=None, soap_cats=u'soapu'):
1164 # get data 1165 cmd = u"select narrative from clin.v_emr_journal where pk_patient=%s and pk_encounter=%s and soap_cat=%s" 1166 for soap_cat in soap_cats: 1167 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': (self.__pat['pk_identity'], encounter['pk_encounter'], soap_cat)}]) 1168 target.write('*MD%s*\r\n' % gmClinNarrative.soap_cat2l10n[soap_cat]) 1169 for row in rows: 1170 text = row[0] 1171 if text is None: 1172 continue 1173 target.write('%s\r\n' % gmTools.wrap ( 1174 text = text, 1175 width = 64, 1176 eol = u'\r\n' 1177 )) 1178 return True
1179 #============================================================ 1180 # main 1181 #------------------------------------------------------------
1182 -def usage():
1183 """ 1184 Prints application usage options to stdout. 1185 """ 1186 print 'usage: python gmPatientExporter [--fileout=<outputfilename>] [--conf-file=<file>] [--text-domain=<textdomain>]' 1187 sys.exit(0)
1188 #------------------------------------------------------------
1189 -def run():
1190 """ 1191 Main module application execution loop. 1192 """ 1193 # More variable initializations 1194 patient = None 1195 patient_id = None 1196 patient_term = None 1197 pat_searcher = gmPersonSearch.cPatientSearcher_SQL() 1198 1199 # App execution loop 1200 while patient_term != 'bye': 1201 patient = gmPersonSearch.ask_for_patient() 1202 if patient is None: 1203 break 1204 # FIXME: needed ? 1205 # gmPerson.set_active_patient(patient=patient) 1206 exporter = cEMRJournalExporter() 1207 exporter.export_to_file(patient=patient) 1208 # export_tool.set_patient(patient) 1209 # Dump patient EMR sections 1210 # export_tool.dump_constraints() 1211 # export_tool.dump_demographic_record(True) 1212 # export_tool.dump_clinical_record() 1213 # export_tool.dump_med_docs() 1214 1215 # Clean ups 1216 # outFile.close() 1217 # export_tool.cleanup() 1218 if patient is not None: 1219 try: 1220 patient.cleanup() 1221 except: 1222 print "error cleaning up patient"
1223 #============================================================ 1224 # main 1225 #------------------------------------------------------------ 1226 if __name__ == "__main__": 1227 gmI18N.activate_locale() 1228 gmI18N.install_domain() 1229 1230 #--------------------------------------------------------
1231 - def export_journal():
1232 1233 print "Exporting EMR journal(s) ..." 1234 pat_searcher = gmPersonSearch.cPatientSearcher_SQL() 1235 while True: 1236 patient = gmPersonSearch.ask_for_patient() 1237 if patient is None: 1238 break 1239 1240 exporter = cEMRJournalExporter() 1241 print "exported into file:", exporter.export_to_file(patient=patient) 1242 1243 if patient is not None: 1244 try: 1245 patient.cleanup() 1246 except: 1247 print "error cleaning up patient" 1248 print "Done."
1249 #--------------------------------------------------------
1250 - def export_forensics():
1251 pat_searcher = gmPersonSearch.cPatientSearcher_SQL() 1252 patient = gmPersonSearch.ask_for_patient() 1253 if patient is None: 1254 return 1255 1256 exporter = cEMRJournalExporter() 1257 print "exported into file:", exporter.export_to_file_by_mod_time(patient = patient)
1258 #-------------------------------------------------------- 1259 print "\n\nGNUmed ASCII EMR Export" 1260 print "=======================" 1261 1262 #export_journal() 1263 export_forensics() 1264 1265 #============================================================ 1266