Gnumed.exporters.gmTimelineExporter

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

# Source Code for Module Gnumed.exporters.gmTimelineExporter

      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 io
     13  import os
     14
     15
     16  if __name__ == '__main__':
     17          sys.path.insert(0, '../../')
     18          from Gnumed.pycommon import gmI18N
     19  from Gnumed.pycommon import gmTools
     20  from Gnumed.pycommon import gmDateTime
     21
     22
     23  _log = logging.getLogger('gm.tl')
     24
     25  #============================================================
     26  ERA_NAME_CARE_PERIOD = _('Care Period')
     27
     28  #============================================================
     29
     30  # <icon>base-64 encoded PNG image data</icon>
     31
     32  #============================================================
     33  xml_start = """<?xml version="1.0" encoding="utf-8"?>
     34  <timeline>
     35          <version>1.20.0</version>
     36          <timetype>gregoriantime</timetype>
     37          <!-- ======================================== Eras ======================================== -->
     38          <eras>
     39                  <era>
     40                          <name>%s</name>
     41                          <start>%s</start>
     42                          <end>%s</end>
     43                          <color>205,238,241</color>
     44                          <ends_today>%s</ends_today>
     45                  </era>
     46                  <era>
     47                          <name>%s</name>
     48                          <start>%s</start>
     49                          <end>%s</end>
     50                          <color>161,210,226</color>
     51                          <ends_today>%s</ends_today>
     52                  </era>
     53          </eras>
     54          <!-- ======================================== Categories ======================================== -->
     55          <categories>
     56                  <!-- health issues -->
     57                  <category>
     58                          <name>%s</name>
     59                          <color>255,0,0</color>
     60                          <font_color>0,0,0</font_color>
     61                  </category>
     62                  <!-- episodes -->
     63                  <category>
     64                          <name>%s</name>
     65                          <color>0,255,0</color>
     66                          <font_color>0,0,0</font_color>
     67                  </category>
     68                  <!-- encounters -->
     69                  <category>
     70                          <name>%s</name>
     71                          <color>30,144,255</color>
     72                          <font_color>0,0,0</font_color>
     73                  </category>
     74                  <!-- hospital stays -->
     75                  <category>
     76                          <name>%s</name>
     77                          <color>255,255,0</color>
     78                          <font_color>0,0,0</font_color>
     79                  </category>
     80                  <!-- procedures -->
     81                  <category>
     82                          <name>%s</name>
     83                          <color>160,32,140</color>
     84                          <font_color>0,0,0</font_color>
     85                  </category>
     86                  <!-- documents -->
     87                  <category>
     88                          <name>%s</name>
     89                          <color>255,165,0</color>
     90                          <font_color>0,0,0</font_color>
     91                  </category>
     92                  <!-- vaccinations -->
     93                  <category>
     94                          <name>%s</name>
     95                          <color>144,238,144</color>
     96                          <font_color>0,0,0</font_color>
     97                  </category>
     98                  <!-- substance intake -->
     99                  <category>
    100                          <name>%s</name>
    101                          <color>165,42,42</color>
    102                          <font_color>0,0,0</font_color>
    103                  </category>
    104                  <!-- life events -->
    105                  <category>
    106                          <name>%s</name>
    107                          <color>30,144,255</color>
    108                          <font_color>0,0,0</font_color>
    109                  </category>
    110          </categories>
    111          <!-- ======================================== Events ======================================== -->
    112          <events>"""
    113
    114  xml_end = """
    115          </events>
    116          <view>
    117                  <displayed_period>
    118                          <start>%s</start>
    119                          <end>%s</end>
    120                  </displayed_period>
    121          <hidden_categories>
    122          </hidden_categories>
    123          </view>
    124  </timeline>"""
    125
    126  #============================================================
    127 -def format_pydt(pydt, format = '%Y-%m-%d %H:%M:%S'):
    128          return gmDateTime.pydt_strftime(pydt, format = format, accuracy = gmDateTime.acc_seconds)
    129
    130  #------------------------------------------------------------
    131  # health issues
    132  #------------------------------------------------------------
    133  __xml_issue_template = """
    134                  <event>
    135                          <start>%(start)s</start>
    136                          <end>%(end)s</end>
    137                          <text>%(container_id)s%(label)s</text>
    138                          <fuzzy>%(fuzzy)s</fuzzy>
    139                          <locked>True</locked>
    140                          <ends_today>%(ends2day)s</ends_today>
    141                          <category>%(category)s</category>
    142                          <description>%(desc)s</description>
    143                  </event>"""
    144
    145 -def __format_health_issue_as_timeline_xml(issue, patient, emr):
    146          # container IDs are supposed to be numeric
    147          # 85bd7a14a1e74aab8db072ff8f417afb@H30.rldata.local
    148          data = {'category': _('Health issues')}
    149          possible_start = issue.possible_start_date
    150          safe_start = issue.safe_start_date
    151          end = issue.clinical_end_date
    152          ends_today = 'False'
    153          if end is None:
    154                  # open episode or active
    155                  ends_today = 'True'
    156                  end = now
    157          # somewhat hacky and not really correct:
    158          start2use = safe_start
    159          if safe_start > end:
    160                  if possible_start < end:
    161                          start2use = possible_start
    162                  else:
    163                          start2use = end
    164          data['desc'] = gmTools.xml_escape_string(issue.format (
    165                  patient = patient,
    166                  with_summary = True,
    167                  with_codes = True,
    168                  with_episodes = True,
    169                  with_encounters = False,
    170                  with_medications = False,
    171                  with_hospital_stays = False,
    172                  with_procedures = False,
    173                  with_family_history = False,
    174                  with_documents = False,
    175                  with_tests = False,
    176                  with_vaccinations = False
    177          ).strip().strip('\n').strip())
    178          label = gmTools.shorten_words_in_line(text = issue['description'], max_length = 25, min_word_length = 5)
    179          xml = ''
    180  #       if possible_start < safe_start:
    181  #               data['start'] = format_pydt(possible_start)
    182  #               data['end'] = format_pydt(safe_start)
    183  #               data['ends2day'] = 'False'
    184  #               data['fuzzy'] = 'True'
    185  #               data['container_id'] = ''
    186  #               data['label'] = '?%s?' % gmTools.xml_escape_string(label)
    187  #               xml += __xml_issue_template % data
    188          data['start'] = format_pydt(start2use)
    189          data['end'] = format_pydt(end)
    190          data['ends2day'] = ends_today
    191          data['fuzzy'] = 'False'
    192          data['container_id'] = '[%s]' % issue['pk_health_issue']
    193          data['label'] = gmTools.xml_escape_string(label)
    194          xml += __xml_issue_template % data
    195          return xml
    196
    197  #------------------------------------------------------------
    198  # episodes
    199  #------------------------------------------------------------
    200  __xml_episode_template = """
    201                  <event>
    202                          <start>%(start)s</start>
    203                          <end>%(end)s</end>
    204                          <text>%(container_id)s%(label)s</text>
    205                          <progress>%(progress)s</progress>
    206                          <fuzzy>False</fuzzy>
    207                          <locked>True</locked>
    208                          <ends_today>%(ends2day)s</ends_today>
    209                          <category>%(category)s</category>
    210                          <description>%(desc)s</description>
    211                  </event>"""
    212
    213 -def __format_episode_as_timeline_xml(episode, patient):
    214          data = {
    215                  'category': _('Episodes'),
    216                  'start': format_pydt(episode.best_guess_clinical_start_date),
    217                  'container_id': gmTools.coalesce (
    218                          value2test = episode['pk_health_issue'],
    219                          return_instead = '',
    220                          template4value = '(%s)'
    221                  ),
    222                  'label': gmTools.xml_escape_string (
    223                          gmTools.shorten_words_in_line(text = episode['description'], max_length = 20, min_word_length = 5)
    224                  ),
    225                  'ends2day': gmTools.bool2subst(episode['episode_open'], 'True', 'False'),
    226                  'progress': gmTools.bool2subst(episode['episode_open'], '0', '100'),
    227                  'desc': gmTools.xml_escape_string(episode.format (
    228                          patient = patient,
    229                          with_summary = True,
    230                          with_codes = True,
    231                          with_encounters = True,
    232                          with_documents = False,
    233                          with_hospital_stays = False,
    234                          with_procedures = False,
    235                          with_family_history = False,
    236                          with_tests = False,
    237                          with_vaccinations = False,
    238                          with_health_issue = True
    239                  ).strip().strip('\n').strip())
    240          }
    241          end = episode.best_guess_clinical_end_date
    242          if end is None:
    243                  data['end'] = format_pydt(now)
    244          else:
    245                  data['end'] = format_pydt(end)
    246          return __xml_episode_template % data
    247
    248  #------------------------------------------------------------
    249  # encounters
    250  #------------------------------------------------------------
    251  __xml_encounter_template = """
    252                  <event>
    253                          <start>%s</start>
    254                          <end>%s</end>
    255                          <text>%s</text>
    256                          <progress>0</progress>
    257                          <fuzzy>False</fuzzy>
    258                          <locked>True</locked>
    259                          <ends_today>False</ends_today>
    260                          <category>%s</category>
    261                          <description>%s</description>
    262                          <milestone>%s</milestone>
    263                  </event>"""
    264
    265 -def __format_encounter_as_timeline_xml(encounter, patient):
    266          return __xml_encounter_template % (
    267                  format_pydt(encounter['started']),
    268                  format_pydt(encounter['last_affirmed']),
    269                  #u'(%s)' % encounter['pk_episode'],
    270                  gmTools.xml_escape_string(format_pydt(encounter['started'], format = '%b %d')),
    271                  _('Encounters'),                                                                                                # category
    272                  gmTools.xml_escape_string(encounter.format (
    273                          patient = patient,
    274                          with_soap = True,
    275                          with_docs = False,
    276                          with_tests = False,
    277                          fancy_header = False,
    278                          with_vaccinations = False,
    279                          with_co_encountlet_hints = False,
    280                          with_rfe_aoe = True,
    281                          with_family_history = False
    282                  ).strip().strip('\n').strip()),
    283                  'False'
    284          )
    285
    286  #------------------------------------------------------------
    287  # hospital stays
    288  #------------------------------------------------------------
    289  __xml_hospital_stay_template = """
    290                  <event>
    291                          <start>%s</start>
    292                          <end>%s</end>
    293                          <text>%s</text>
    294                          <fuzzy>False</fuzzy>
    295                          <locked>True</locked>
    296                          <ends_today>False</ends_today>
    297                          <category>%s</category>
    298                          <description>%s</description>
    299                  </event>"""
    300
    301 -def __format_hospital_stay_as_timeline_xml(stay):
    302          end = stay['discharge']
    303          if end is None:
    304                  end = now
    305          return __xml_hospital_stay_template % (
    306                  format_pydt(stay['admission']),
    307                  format_pydt(end),
    308                  gmTools.xml_escape_string(stay['hospital']),
    309                  _('Hospital stays'),                                                                                            # category
    310                  gmTools.xml_escape_string(stay.format().strip().strip('\n').strip())
    311          )
    312
    313  #------------------------------------------------------------
    314  # procedures
    315  #------------------------------------------------------------
    316  __xml_procedure_template = """
    317                  <event>
    318                          <start>%s</start>
    319                          <end>%s</end>
    320                          <text>%s</text>
    321                          <fuzzy>False</fuzzy>
    322                          <locked>True</locked>
    323                          <ends_today>False</ends_today>
    324                          <category>%s</category>
    325                          <description>%s</description>
    326                  </event>"""
    327
    328 -def __format_procedure_as_timeline_xml(proc):
    329          if proc['is_ongoing']:
    330                  end = now
    331          else:
    332                  if proc['clin_end'] is None:
    333                          end = proc['clin_when']
    334                  else:
    335                          end = proc['clin_end']
    336          desc = gmTools.shorten_words_in_line(text = proc['performed_procedure'], max_length = 20, min_word_length = 5)
    337          return __xml_procedure_template % (
    338                  format_pydt(proc['clin_when']),
    339                  format_pydt(end),
    340                  gmTools.xml_escape_string(desc),
    341                  _('Procedures'),
    342                  gmTools.xml_escape_string(proc.format (
    343                          include_episode = True,
    344                          include_codes = True
    345                  ).strip().strip('\n').strip())
    346          )
    347
    348  #------------------------------------------------------------
    349  # documents
    350  #------------------------------------------------------------
    351  __xml_document_template = """
    352                  <event>
    353                          <start>%s</start>
    354                          <end>%s</end>
    355                          <text>%s</text>
    356                          <fuzzy>False</fuzzy>
    357                          <locked>True</locked>
    358                          <ends_today>False</ends_today>
    359                          <category>%s</category>
    360                          <description>%s</description>
    361                  </event>"""
    362
    363 -def __format_document_as_timeline_xml(doc):
    364          desc = gmTools.shorten_words_in_line(text = doc['l10n_type'], max_length = 20, min_word_length = 5)
    365          return __xml_document_template % (
    366                  format_pydt(doc['clin_when']),
    367                  format_pydt(doc['clin_when']),
    368                  gmTools.xml_escape_string(desc),
    369                  _('Documents'),
    370                  gmTools.xml_escape_string(doc.format().strip().strip('\n').strip())
    371          )
    372
    373  #------------------------------------------------------------
    374  # vaccinations
    375  #------------------------------------------------------------
    376  __xml_vaccination_template = """
    377                  <event>
    378                          <start>%s</start>
    379                          <end>%s</end>
    380                          <text>%s</text>
    381                          <fuzzy>False</fuzzy>
    382                          <locked>True</locked>
    383                          <ends_today>False</ends_today>
    384                          <category>%s</category>
    385                          <description>%s</description>
    386                  </event>"""
    387
    388 -def __format_vaccination_as_timeline_xml(vacc):
    389          return __xml_vaccination_template % (
    390                  format_pydt(vacc['date_given']),
    391                  format_pydt(vacc['date_given']),
    392                  gmTools.xml_escape_string(vacc['vaccine']),
    393                  _('Vaccinations'),
    394                  gmTools.xml_escape_string('\n'.join(vacc.format (
    395                          with_indications = True,
    396                          with_comment = True,
    397                          with_reaction = True,
    398                          date_format = '%Y %b %d'
    399                  )).strip().strip('\n').strip())
    400          )
    401
    402  #------------------------------------------------------------
    403  # substance intake
    404  #------------------------------------------------------------
    405  __xml_intake_template = """
    406                  <event>
    407                          <start>%s</start>
    408                          <end>%s</end>
    409                          <text>%s</text>
    410                          <fuzzy>False</fuzzy>
    411                          <locked>True</locked>
    412                          <ends_today>False</ends_today>
    413                          <category>%s</category>
    414                          <description>%s</description>
    415                  </event>"""
    416
    417 -def __format_intake_as_timeline_xml(intake):
    418          if intake['discontinued'] is None:
    419                  if intake['duration'] is None:
    420                          if intake['seems_inactive']:
    421                                  end = intake['started']
    422                          else:
    423                                  end = now
    424                  else:
    425                          end = intake['started'] + intake['duration']
    426          else:
    427                  end = intake['discontinued']
    428
    429          return __xml_intake_template % (
    430                  format_pydt(intake['started']),
    431                  format_pydt(end),
    432                  gmTools.xml_escape_string(intake['substance']),
    433                  _('Substances'),
    434                  gmTools.xml_escape_string(intake.format (
    435                          single_line = False,
    436                          show_all_product_components = False
    437                  ).strip().strip('\n').strip())
    438          )
    439
    440  #------------------------------------------------------------
    441  # main library entry point
    442  #------------------------------------------------------------
    443 -def create_timeline_file(patient=None, filename=None, include_documents=False, include_vaccinations=False, include_encounters=False):
    444
    445          emr = patient.emr
    446          global now
    447          now = gmDateTime.pydt_now_here()
    448
    449          if filename is None:
    450                  timeline_fname = gmTools.get_unique_filename(prefix = 'gm-', suffix = '.timeline')      # .timeline required ...
    451          else:
    452                  timeline_fname = filename
    453          _log.debug('exporting EMR as timeline into [%s]', timeline_fname)
    454          timeline = io.open(timeline_fname, mode = 'wt', encoding = 'utf8', errors = 'xmlcharrefreplace')
    455
    456          if patient['dob'] is None:
    457                  lifespan_start = format_pydt(now.replace(year = now.year - 100))
    458          else:
    459                  lifespan_start = format_pydt(patient['dob'])
    460
    461          if patient['deceased'] is None:
    462                  life_ends2day = 'True'
    463                  lifespan_end = format_pydt(now)
    464          else:
    465                  life_ends2day = 'False'
    466                  lifespan_end = format_pydt(patient['deceased'])
    467
    468          earliest_care_date = emr.earliest_care_date
    469          most_recent_care_date = emr.most_recent_care_date
    470          if most_recent_care_date is None:
    471                  most_recent_care_date = lifespan_end
    472                  care_ends2day = life_ends2day
    473          else:
    474                  most_recent_care_date = format_pydt(most_recent_care_date)
    475                  care_ends2day = 'False'
    476
    477          timeline.write(xml_start % (
    478                  # era: life span of patient
    479                  _('Lifespan'),
    480                  lifespan_start,
    481                  lifespan_end,
    482                  life_ends2day,
    483                  ERA_NAME_CARE_PERIOD,
    484                  format_pydt(earliest_care_date),
    485                  most_recent_care_date,
    486                  care_ends2day,
    487                  # categories
    488                  _('Health issues'),
    489                  _('Episodes'),
    490                  _('Encounters'),
    491                  _('Hospital stays'),
    492                  _('Procedures'),
    493                  _('Documents'),
    494                  _('Vaccinations'),
    495                  _('Substances'),
    496                  _('Life events')
    497          ))
    498          # birth
    499          if patient['dob'] is None:
    500                  start = now.replace(year = now.year - 100)
    501                  timeline.write(__xml_encounter_template % (
    502                          format_pydt(start),
    503                          format_pydt(start),
    504                          '?',
    505                          _('Life events'),
    506                          _('Date of birth unknown'),
    507                          'True'
    508                  ))
    509          else:
    510                  start = patient['dob']
    511                  timeline.write(__xml_encounter_template % (
    512                          format_pydt(patient['dob']),
    513                          format_pydt(patient['dob']),
    514                          '*',
    515                          _('Life events'),
    516                          _('Birth: %s') % patient.get_formatted_dob(format = '%Y %b %d %H:%M', honor_estimation = True),
    517                          'True'
    518                  ))
    519
    520          # start of care
    521          timeline.write(__xml_encounter_template % (
    522                  format_pydt(earliest_care_date),
    523                  format_pydt(earliest_care_date),
    524                  gmTools.u_heavy_greek_cross,
    525                  _('Life events'),
    526                  _('Start of Care: %s\n(the earliest recorded event of care in this praxis)') % format_pydt(earliest_care_date, format = '%Y %b %d'),
    527                  'True'
    528          ))
    529
    530          # containers must be defined before their
    531          # subevents, so put health issues first
    532          timeline.write('\n              <!-- ========================================\n Health issues\n======================================== -->')
    533          for issue in emr.health_issues:
    534                  timeline.write(__format_health_issue_as_timeline_xml(issue, patient, emr))
    535
    536          timeline.write('\n              <!-- ========================================\n Episodes\n======================================== -->')
    537          for epi in emr.get_episodes(order_by = 'pk_health_issue'):
    538                  timeline.write(__format_episode_as_timeline_xml(epi, patient))
    539
    540          if include_encounters:
    541                  timeline.write(u'\n<!--\n========================================\n Encounters\n======================================== -->')
    542                  for enc in emr.get_encounters(skip_empty = True):
    543                          timeline.write(__format_encounter_as_timeline_xml(enc, patient))
    544
    545          timeline.write('\n<!--\n========================================\n Hospital stays\n======================================== -->')
    546          for stay in emr.hospital_stays:
    547                  timeline.write(__format_hospital_stay_as_timeline_xml(stay))
    548
    549          timeline.write('\n<!--\n========================================\n Procedures\n======================================== -->')
    550          for proc in emr.performed_procedures:
    551                  timeline.write(__format_procedure_as_timeline_xml(proc))
    552
    553          if include_vaccinations:
    554                  timeline.write(u'\n<!--\n========================================\n Vaccinations\n======================================== -->')
    555                  for vacc in emr.vaccinations:
    556                          timeline.write(__format_vaccination_as_timeline_xml(vacc))
    557
    558          timeline.write('\n<!--\n========================================\n Substance intakes\n======================================== -->')
    559          for intake in emr.get_current_medications(include_inactive = True, include_unapproved = False):
    560                  timeline.write(__format_intake_as_timeline_xml(intake))
    561
    562          if include_documents:
    563                  timeline.write(u'\n<!--\n========================================\n Documents\n======================================== -->')
    564                  for doc in patient.document_folder.documents:
    565                          timeline.write(__format_document_as_timeline_xml(doc))
    566
    567          # allergies ?
    568          # - unclear where and how to place
    569          # test results ?
    570          # - too many events, at most "day sample drawn"
    571
    572          # death
    573          if patient['deceased'] is None:
    574                  end = now
    575          else:
    576                  end = patient['deceased']
    577                  death_ago = gmDateTime.format_apparent_age_medically (
    578                          age = gmDateTime.calculate_apparent_age(start = end, end = now)
    579                  )
    580                  timeline.write(__xml_encounter_template % (
    581                          format_pydt(end),
    582                          format_pydt(end),
    583                          gmTools.u_dagger,
    584                          _('Life events'),
    585                          _('Death: %s\n(%s ago at age %s)') % (
    586                                  format_pydt(end, format = '%Y %b %d %H:%M'),
    587                                  death_ago,
    588                                  patient.get_medical_age()
    589                          ),
    590                          'True'
    591                  ))
    592
    593          # display range
    594          if end.month == 2:
    595                  if end.day == 29:
    596                          # leap years aren't consecutive
    597                          end = end.replace(day = 28)
    598          target_year = end.year + 1
    599          end = end.replace(year = target_year)
    600          timeline.write(xml_end % (
    601                  format_pydt(start),
    602                  format_pydt(end)
    603          ))
    604
    605          timeline.close()
    606          return timeline_fname
    607
    608  #------------------------------------------------------------
    609  __fake_timeline_start = """<?xml version="1.0" encoding="utf-8"?>
    610  <timeline>
    611          <version>0.20.0</version>
    612          <categories>
    613                  <!-- life events -->
    614                  <category>
    615                          <name>%s</name>
    616                          <color>30,144,255</color>
    617                          <font_color>0,0,0</font_color>
    618                  </category>
    619          </categories>
    620          <events>""" % _('Life events')
    621
    622  __fake_timeline_body_template = """
    623                  <event>
    624                          <start>%s</start>
    625                          <end>%s</end>
    626                          <text>%s</text>
    627                          <fuzzy>False</fuzzy>
    628                          <locked>True</locked>
    629                          <ends_today>False</ends_today>
    630                          <!-- category></category -->
    631                          <description>%s
    632                          </description>
    633                  </event>"""
    634
    635 -def create_fake_timeline_file(patient=None, filename=None):
    636          """Used to create an 'empty' timeline file for display.
    637
    638          - needed because .clear_timeline() doesn't really work
    639          """
    640          emr = patient.emr
    641          global now
    642          now = gmDateTime.pydt_now_here()
    643
    644          if filename is None:
    645                  timeline_fname = gmTools.get_unique_filename(prefix = 'gm-', suffix = '.timeline')
    646          else:
    647                  timeline_fname = filename
    648
    649          _log.debug('creating dummy timeline in [%s]', timeline_fname)
    650          timeline = io.open(timeline_fname, mode = 'wt', encoding = 'utf8', errors = 'xmlcharrefreplace')
    651
    652          timeline.write(__fake_timeline_start)
    653
    654          # birth
    655          if patient['dob'] is None:
    656                  start = now.replace(year = now.year - 100)
    657                  timeline.write(__xml_encounter_template % (
    658                          format_pydt(start),
    659                          format_pydt(start),
    660                          _('Birth') + ': ?',
    661                          _('Life events'),
    662                          _('Date of birth unknown'),
    663                          'False'
    664                  ))
    665          else:
    666                  start = patient['dob']
    667                  timeline.write(__xml_encounter_template % (
    668                          format_pydt(patient['dob']),
    669                          format_pydt(patient['dob']),
    670                          _('Birth') + gmTools.bool2subst(patient['dob_is_estimated'], ' (%s)' % gmTools.u_almost_equal_to, ''),
    671                          _('Life events'),
    672                          '',
    673                          'False'
    674                  ))
    675
    676          # death
    677          if patient['deceased'] is None:
    678                  end = now
    679          else:
    680                  end = patient['deceased']
    681                  timeline.write(__xml_encounter_template % (
    682                          format_pydt(end),
    683                          format_pydt(end),
    684                          #u'',
    685                          _('Death'),
    686                          _('Life events'),
    687                          '',
    688                          'False'
    689                  ))
    690
    691          # fake issue
    692          timeline.write(__fake_timeline_body_template % (
    693                  format_pydt(start),
    694                  format_pydt(end),
    695                  _('Cannot display timeline.'),
    696                  _('Cannot display timeline.')
    697          ))
    698
    699          # display range
    700          if end.month == 2:
    701                  if end.day == 29:
    702                          # leap years aren't consecutive
    703                          end = end.replace(day = 28)
    704          target_year = end.year + 1
    705          end = end.replace(year = target_year)
    706          timeline.write(xml_end % (
    707                  format_pydt(start),
    708                  format_pydt(end)
    709          ))
    710
    711          timeline.close()
    712          return timeline_fname
    713
    714  #============================================================
    715  # main
    716  #------------------------------------------------------------
    717  if __name__ == '__main__':
    718
    719          if len(sys.argv) < 2:
    720                  sys.exit()
    721
    722          if sys.argv[1] != "test":
    723                  sys.exit()
    724
    725          gmI18N.activate_locale()
    726          gmI18N.install_domain('gnumed')
    727
    728          from Gnumed.business import gmPraxis
    729          praxis = gmPraxis.gmCurrentPraxisBranch(branch = gmPraxis.get_praxis_branches()[0])
    730
    731          from Gnumed.business import gmPerson
    732          # 14 / 20 / 138 / 58 / 20 / 5
    733          pat = gmPerson.gmCurrentPatient(gmPerson.cPatient(aPK_obj = 14))
    734          fname = '~/gnumed/gm2tl-%s.timeline' % pat.subdir_name
    735
    736          print (create_timeline_file (
    737                  patient = pat,
    738                  filename = os.path.expanduser(fname),
    739                  include_documents = True,
    740                  include_vaccinations = True,
    741                  include_encounters = True
    742          ))
    743