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

Source Code for Module Gnumed.exporters.gmPatientExporter

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