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

Source Code for Module Gnumed.exporters.timeline

  1  # -*- coding: utf8 -*- 
  2  """Timeline exporter. 
  3   
  4  Copyright: authors 
  5  """ 
  6  #============================================================ 
  7  __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>" 
  8  __license__ = 'GPL v2 or later (details at http://www.gnu.org)' 
  9   
 10  import sys 
 11  import logging 
 12  import codecs 
 13  import os 
 14   
 15   
 16  if __name__ == '__main__': 
 17          sys.path.insert(0, '../../') 
 18  from Gnumed.pycommon import gmTools 
 19  from Gnumed.pycommon import gmDateTime 
 20   
 21   
 22  _log = logging.getLogger('gm.tl') 
 23   
 24  #============================================================ 
 25  xml_start = u"""<?xml version="1.0" encoding="utf-8"?> 
 26  <timeline> 
 27          <version>0.17.0</version> 
 28          <categories> 
 29                  <!-- health issues --> 
 30                  <category> 
 31                          <name>%s</name> 
 32                          <color>255,0,0</color> 
 33                          <font_color>0,0,0</font_color> 
 34                  </category> 
 35                  <!-- episodes --> 
 36                  <category> 
 37                          <name>%s</name> 
 38                          <color>0,255,0</color> 
 39                          <font_color>0,0,0</font_color> 
 40                  </category> 
 41                  <!-- encounters --> 
 42                  <category> 
 43                          <name>%s</name> 
 44                          <color>30,144,255</color> 
 45                          <font_color>0,0,0</font_color> 
 46                  </category> 
 47                  <!-- hospital stays --> 
 48                  <category> 
 49                          <name>%s</name> 
 50                          <color>255,255,0</color> 
 51                          <font_color>0,0,0</font_color> 
 52                  </category> 
 53                  <!-- procedures --> 
 54                  <category> 
 55                          <name>%s</name> 
 56                          <color>160,32,140</color> 
 57                          <font_color>0,0,0</font_color> 
 58                  </category> 
 59                  <!-- documents --> 
 60                  <category> 
 61                          <name>%s</name> 
 62                          <color>255,165,0</color> 
 63                          <font_color>0,0,0</font_color> 
 64                  </category> 
 65                  <!-- vaccinations --> 
 66                  <category> 
 67                          <name>%s</name> 
 68                          <color>144,238,144</color> 
 69                          <font_color>0,0,0</font_color> 
 70                  </category> 
 71                  <!-- substance intake --> 
 72                  <category> 
 73                          <name>%s</name> 
 74                          <color>165,42,42</color> 
 75                          <font_color>0,0,0</font_color> 
 76                  </category> 
 77                  <!-- life events --> 
 78                  <category> 
 79                          <name>%s</name> 
 80                          <color>30,144,255</color> 
 81                          <font_color>0,0,0</font_color> 
 82                  </category> 
 83          </categories> 
 84          <events>""" 
 85   
 86  xml_end = u""" 
 87          </events> 
 88          <view> 
 89                  <displayed_period> 
 90                          <start>%s</start> 
 91                          <end>%s</end> 
 92                  </displayed_period> 
 93          <hidden_categories> 
 94          </hidden_categories> 
 95          </view> 
 96  </timeline>""" 
 97   
 98  #============================================================ 
99 -def format_pydt(pydt, format = '%Y-%m-%d %H:%M:%S'):
100 return gmDateTime.pydt_strftime(pydt, format = format, accuracy = gmDateTime.acc_seconds)
101 102 #------------------------------------------------------------ 103 # health issues 104 #------------------------------------------------------------ 105 __xml_issue_template = u""" 106 <event> 107 <start>%s</start> 108 <end>%s</end> 109 <text>%s</text> 110 <fuzzy>False</fuzzy> 111 <locked>True</locked> 112 <ends_today>%s</ends_today> 113 <category>%s</category> 114 <description>%s 115 </description> 116 </event>""" 117
118 -def __format_health_issue_as_timeline_xml(issue, patient, emr):
119 tooltip = issue.format ( 120 patient = patient, 121 with_summary = True, 122 with_codes = True, 123 with_episodes = True, 124 with_encounters = False, 125 with_medications = False, 126 with_hospital_stays = False, 127 with_procedures = False, 128 with_family_history = False, 129 with_documents = False, 130 with_tests = False, 131 with_vaccinations = False 132 ) 133 safe_start = issue.safe_start_date 134 possible_start = issue.possible_start_date 135 txt = u'' 136 if possible_start < safe_start: 137 txt += __xml_issue_template % ( 138 format_pydt(possible_start), # start 139 format_pydt(safe_start), # end 140 gmTools.xml_escape_string(u'?[%s]?' % issue['description']), # text 141 u'False', # ends_today 142 _('Health issues'), # category 143 gmTools.xml_escape_string(tooltip) # description 144 ) 145 txt += __xml_issue_template % ( 146 format_pydt(safe_start), # start 147 format_pydt(issue.end_date), # end 148 gmTools.xml_escape_string(issue['description']), # text 149 gmTools.bool2subst(issue['is_active'], u'True', u'False'), # ends_today 150 _('Health issues'), # category 151 gmTools.xml_escape_string(tooltip) # description 152 ) 153 return txt
154 #------------------------------------------------------------ 155 # episodes 156 #------------------------------------------------------------ 157 __xml_episode_template = u""" 158 <event> 159 <start>%s</start> 160 <end>%s</end> 161 <text>%s</text> 162 <fuzzy>False</fuzzy> 163 <locked>True</locked> 164 <ends_today>%s</ends_today> 165 <category>%s</category> 166 <description>%s 167 </description> 168 </event>""" 169
170 -def __format_episode_as_timeline_xml(episode, patient):
171 end = gmTools.bool2subst ( 172 episode['episode_open'], 173 format_pydt(now), 174 format_pydt(episode.latest_access_date), 175 ) 176 return __xml_episode_template % ( 177 format_pydt(episode.best_guess_start_date), # start 178 end, # end 179 gmTools.xml_escape_string(episode['description']), # text 180 gmTools.bool2subst(episode['episode_open'], u'True', u'False'), # ends_today 181 _('Episodes'), # category 182 gmTools.xml_escape_string(episode.format ( # description 183 patient = patient, 184 with_summary = True, 185 with_codes = True, 186 with_encounters = True, 187 with_documents = False, 188 with_hospital_stays = False, 189 with_procedures = False, 190 with_family_history = False, 191 with_tests = False, 192 with_vaccinations = False, 193 with_health_issue = True 194 )) 195 )
196 197 #------------------------------------------------------------ 198 # encounters 199 #------------------------------------------------------------ 200 __xml_encounter_template = u""" 201 <event> 202 <start>%s</start> 203 <end>%s</end> 204 <text>%s</text> 205 <fuzzy>False</fuzzy> 206 <locked>True</locked> 207 <ends_today>False</ends_today> 208 <category>%s</category> 209 <description>%s 210 </description> 211 </event>""" 212
213 -def __format_encounter_as_timeline_xml(encounter, patient):
214 return __xml_encounter_template % ( 215 format_pydt(encounter['started']), 216 format_pydt(encounter['last_affirmed']), 217 #u'(%s)' % encounter['pk_episode'], 218 gmTools.xml_escape_string(format_pydt(encounter['started'], format = '%b %d')), 219 _('Encounters'), # category 220 gmTools.xml_escape_string(encounter.format ( 221 patient = patient, 222 with_soap = True, 223 with_docs = False, 224 with_tests = False, 225 fancy_header = False, 226 with_vaccinations = False, 227 with_co_encountlet_hints = False, 228 with_rfe_aoe = True, 229 with_family_history = False 230 )) 231 )
232 233 #------------------------------------------------------------ 234 # hospital stays 235 #------------------------------------------------------------ 236 __xml_hospital_stay_template = u""" 237 <event> 238 <start>%s</start> 239 <end>%s</end> 240 <text>%s</text> 241 <fuzzy>False</fuzzy> 242 <locked>True</locked> 243 <ends_today>False</ends_today> 244 <category>%s</category> 245 <description>%s 246 </description> 247 </event>""" 248
249 -def __format_hospital_stay_as_timeline_xml(stay):
250 end = stay['discharge'] 251 if end is None: 252 end = now 253 return __xml_hospital_stay_template % ( 254 format_pydt(stay['admission']), 255 format_pydt(end), 256 gmTools.xml_escape_string(stay['hospital']), 257 _('Hospital stays'), # category 258 gmTools.xml_escape_string(stay.format()) 259 )
260 261 #------------------------------------------------------------ 262 # procedures 263 #------------------------------------------------------------ 264 __xml_procedure_template = u""" 265 <event> 266 <start>%s</start> 267 <end>%s</end> 268 <text>%s</text> 269 <fuzzy>False</fuzzy> 270 <locked>True</locked> 271 <ends_today>False</ends_today> 272 <category>%s</category> 273 <description>%s 274 </description> 275 </event>""" 276
277 -def __format_procedure_as_timeline_xml(proc):
278 if proc['is_ongoing']: 279 end = now 280 else: 281 if proc['clin_end'] is None: 282 end = proc['clin_when'] 283 else: 284 end = proc['clin_end'] 285 return __xml_procedure_template % ( 286 format_pydt(proc['clin_when']), 287 format_pydt(end), 288 gmTools.xml_escape_string(proc['performed_procedure']), 289 _('Procedures'), 290 gmTools.xml_escape_string(proc.format ( 291 include_episode = True, 292 include_codes = True 293 )) 294 )
295 296 #------------------------------------------------------------ 297 # documents 298 #------------------------------------------------------------ 299 __xml_document_template = u""" 300 <event> 301 <start>%s</start> 302 <end>%s</end> 303 <text>%s</text> 304 <fuzzy>False</fuzzy> 305 <locked>True</locked> 306 <ends_today>False</ends_today> 307 <category>%s</category> 308 <description>%s 309 </description> 310 </event>""" 311
312 -def __format_document_as_timeline_xml(doc):
313 return __xml_document_template % ( 314 format_pydt(doc['clin_when']), 315 format_pydt(doc['clin_when']), 316 gmTools.xml_escape_string(doc['l10n_type']), 317 _('Documents'), 318 gmTools.xml_escape_string(doc.format()) 319 )
320 321 #------------------------------------------------------------ 322 # vaccinations 323 #------------------------------------------------------------ 324 __xml_vaccination_template = u""" 325 <event> 326 <start>%s</start> 327 <end>%s</end> 328 <text>%s</text> 329 <fuzzy>False</fuzzy> 330 <locked>True</locked> 331 <ends_today>False</ends_today> 332 <category>%s</category> 333 <description>%s 334 </description> 335 </event>""" 336
337 -def __format_vaccination_as_timeline_xml(vacc):
338 return __xml_vaccination_template % ( 339 format_pydt(vacc['date_given']), 340 format_pydt(vacc['date_given']), 341 gmTools.xml_escape_string(vacc['vaccine']), 342 _('Vaccinations'), 343 gmTools.xml_escape_string(u'\n'.join(vacc.format ( 344 with_indications = True, 345 with_comment = True, 346 with_reaction = True, 347 date_format = '%Y %b %d' 348 ))) 349 )
350 351 #------------------------------------------------------------ 352 # substance intakt 353 #------------------------------------------------------------ 354 __xml_intake_template = u""" 355 <event> 356 <start>%s</start> 357 <end>%s</end> 358 <text>%s</text> 359 <fuzzy>False</fuzzy> 360 <locked>True</locked> 361 <ends_today>False</ends_today> 362 <category>%s</category> 363 <description>%s 364 </description> 365 </event>""" 366
367 -def __format_intake_as_timeline_xml(intake):
368 if intake['discontinued'] is None: 369 if intake['duration'] is None: 370 if intake['seems_inactive']: 371 end = intake['started'] 372 else: 373 end = now 374 else: 375 end = intake['started'] + intake['duration'] 376 else: 377 end = intake['discontinued'] 378 379 return __xml_intake_template % ( 380 format_pydt(intake['started']), 381 format_pydt(end), 382 gmTools.xml_escape_string(intake['substance']), 383 _('Substances'), 384 gmTools.xml_escape_string(intake.format ( 385 one_line = False, 386 show_all_brand_components = False 387 )) 388 )
389 390 #------------------------------------------------------------ 391 # library entry point 392 #------------------------------------------------------------
393 -def create_timeline_file(patient=None, filename=None):
394 395 emr = patient.emr 396 global now 397 now = gmDateTime.pydt_now_here() 398 399 if filename is None: 400 timeline_fname = gmTools.get_unique_filename(prefix = u'gm-', suffix = u'.timeline') 401 else: 402 timeline_fname = filename 403 _log.debug('exporting EMR as timeline into [%s]', timeline_fname) 404 timeline = codecs.open(timeline_fname, mode = 'wb', encoding = 'utf8', errors = 'xmlcharrefreplace') 405 timeline.write(xml_start % ( 406 _('Health issues'), 407 _('Episodes'), 408 _('Encounters'), 409 _('Hospital stays'), 410 _('Procedures'), 411 _('Documents'), 412 _('Vaccinations'), 413 _('Substances'), 414 _('Life events') 415 )) 416 # birth 417 if patient['dob'] is None: 418 start = now.replace(year = now.year - 100) 419 timeline.write(__xml_encounter_template % ( 420 format_pydt(start), 421 format_pydt(start), 422 _('Birth') + u': ?', 423 _('Life events'), 424 _('Date of birth unknown') 425 )) 426 else: 427 start = patient['dob'] 428 timeline.write(__xml_encounter_template % ( 429 format_pydt(patient['dob']), 430 format_pydt(patient['dob']), 431 _('Birth') + gmTools.bool2subst(patient['dob_is_estimated'], u' (%s)' % gmTools.u_almost_equal_to, u''), 432 _('Life events'), 433 u'' 434 )) 435 436 # start of care 437 timeline.write(__xml_encounter_template % ( 438 format_pydt(emr.earliest_care_date), 439 format_pydt(emr.earliest_care_date), 440 _('Start of Care'), 441 _('Life events'), 442 _('The earliest recorded event of care in this praxis.') 443 )) 444 445 timeline.write(u'\n<!--\n========================================\n Health issues\n======================================== -->') 446 for issue in emr.health_issues: 447 timeline.write(__format_health_issue_as_timeline_xml(issue, patient, emr)) 448 449 timeline.write(u'\n<!--\n========================================\n Episodes\n======================================== -->') 450 for epi in emr.get_episodes(order_by = u'pk_health_issue'): 451 timeline.write(__format_episode_as_timeline_xml(epi, patient)) 452 453 timeline.write(u'\n<!--\n========================================\n Encounters\n======================================== -->') 454 for enc in emr.get_encounters(skip_empty = True): 455 timeline.write(__format_encounter_as_timeline_xml(enc, patient)) 456 457 timeline.write(u'\n<!--\n========================================\n Hospital stays\n======================================== -->') 458 for stay in emr.hospital_stays: 459 timeline.write(__format_hospital_stay_as_timeline_xml(stay)) 460 461 timeline.write(u'\n<!--\n========================================\n Procedures\n======================================== -->') 462 for proc in emr.performed_procedures: 463 timeline.write(__format_procedure_as_timeline_xml(proc)) 464 465 timeline.write(u'\n<!--\n========================================\n Vaccinations\n======================================== -->') 466 for vacc in emr.vaccinations: 467 timeline.write(__format_vaccination_as_timeline_xml(vacc)) 468 469 timeline.write(u'\n<!--\n========================================\n Substance intakes\n======================================== -->') 470 for intake in emr.get_current_substance_intakes(include_inactive = True, include_unapproved = False): 471 timeline.write(__format_intake_as_timeline_xml(intake)) 472 473 timeline.write(u'\n<!--\n========================================\n Documents\n======================================== -->') 474 for doc in patient.document_folder.documents: 475 timeline.write(__format_document_as_timeline_xml(doc)) 476 477 # allergies ? 478 # test results ? 479 480 # death 481 if patient['deceased'] is None: 482 end = now 483 else: 484 end = patient['deceased'] 485 timeline.write(__xml_encounter_template % ( 486 format_pydt(end), 487 format_pydt(end), 488 #u'', 489 _('Death'), 490 _('Life events'), 491 u'' 492 )) 493 494 # display range 495 if end.month == 2: 496 if end.day == 29: 497 # leap years aren't consecutive 498 end = end.replace(day = 28) 499 target_year = end.year + 1 500 end = end.replace(year = target_year) 501 timeline.write(xml_end % ( 502 format_pydt(start), 503 format_pydt(end) 504 )) 505 timeline.close() 506 return timeline_fname
507 508 #------------------------------------------------------------ 509 __fake_timeline_start = u"""<?xml version="1.0" encoding="utf-8"?> 510 <timeline> 511 <version>0.20.0</version> 512 <categories> 513 <!-- life events --> 514 <category> 515 <name>%s</name> 516 <color>30,144,255</color> 517 <font_color>0,0,0</font_color> 518 </category> 519 </categories> 520 <events>""" % _('Life events') 521 522 __fake_timeline_body_template = u""" 523 <event> 524 <start>%s</start> 525 <end>%s</end> 526 <text>%s</text> 527 <fuzzy>False</fuzzy> 528 <locked>True</locked> 529 <ends_today>False</ends_today> 530 <!-- category></category --> 531 <description>%s 532 </description> 533 </event>""" 534
535 -def create_fake_timeline_file(patient=None, filename=None):
536 """Used to create an 'empty' timeline file for display. 537 538 - needed because .clear_timeline() doesn't really work 539 """ 540 emr = patient.emr 541 global now 542 now = gmDateTime.pydt_now_here() 543 544 if filename is None: 545 timeline_fname = gmTools.get_unique_filename(prefix = u'gm-', suffix = u'.timeline') 546 else: 547 timeline_fname = filename 548 549 _log.debug('creating dummy timeline in [%s]', timeline_fname) 550 timeline = codecs.open(timeline_fname, mode = 'wb', encoding = 'utf8', errors = 'xmlcharrefreplace') 551 552 timeline.write(__fake_timeline_start) 553 554 # birth 555 if patient['dob'] is None: 556 start = now.replace(year = now.year - 100) 557 timeline.write(__xml_encounter_template % ( 558 format_pydt(start), 559 format_pydt(start), 560 _('Birth') + u': ?', 561 _('Life events'), 562 _('Date of birth unknown') 563 )) 564 else: 565 start = patient['dob'] 566 timeline.write(__xml_encounter_template % ( 567 format_pydt(patient['dob']), 568 format_pydt(patient['dob']), 569 _('Birth') + gmTools.bool2subst(patient['dob_is_estimated'], u' (%s)' % gmTools.u_almost_equal_to, u''), 570 _('Life events'), 571 u'' 572 )) 573 574 # death 575 if patient['deceased'] is None: 576 end = now 577 else: 578 end = patient['deceased'] 579 timeline.write(__xml_encounter_template % ( 580 format_pydt(end), 581 format_pydt(end), 582 #u'', 583 _('Death'), 584 _('Life events'), 585 u'' 586 )) 587 588 # fake issue 589 timeline.write(__fake_timeline_body_template % ( 590 format_pydt(start), 591 format_pydt(end), 592 _('Cannot display timeline.'), 593 _('Cannot display timeline.') 594 )) 595 596 # display range 597 if end.month == 2: 598 if end.day == 29: 599 # leap years aren't consecutive 600 end = end.replace(day = 28) 601 target_year = end.year + 1 602 end = end.replace(year = target_year) 603 timeline.write(xml_end % ( 604 format_pydt(start), 605 format_pydt(end) 606 )) 607 608 timeline.close() 609 return timeline_fname
610 611 #============================================================ 612 # main 613 #------------------------------------------------------------ 614 if __name__ == '__main__': 615 616 if len(sys.argv) < 2: 617 sys.exit() 618 619 if sys.argv[1] != "test": 620 sys.exit() 621 622 from Gnumed.pycommon import gmI18N 623 gmI18N.activate_locale() 624 gmI18N.install_domain('gnumed') 625 from Gnumed.business import gmPerson 626 # 14 / 20 / 138 / 58 / 20 / 5 627 pat = gmPerson.gmCurrentPatient(gmPerson.cPatient(aPK_obj = 12)) 628 fname = u'~/tmp/gm2tl-%s.timeline' % pat.get_dirname() 629 630 print create_timeline_file(patient = pat, filename = os.path.expanduser(fname)) 631