'''
Benjamin Langley
Usage: python3 R4CapStatement_NarrativeMaker.py [json file]
Dependecies:
fhirclient
pandas
xlrd
openpyxl
stringcase
jinja2
commonmark
lxml
To install all dependencies: pip3 install -r requirements.txt
to run on windows: python -m pip ...
NOTE: this requires the r4models to be installed in the fhirclient pip site-package, to be installed in [installdir]/lib/python/site-packages/fhirclient
Email Eric Haas for these models (the _element extension elements are not supported in that version. Corety Spears can provide a modified set or rfmodels for
the capabilitystatement element level extension )
Modified from:
https://github.com/Healthedata1/MyNotebooks/blob/master/CapStatement/R4CapStatement_Maker.ipynb
and jinja template from
https://github.com/Healthedata1/MyNotebooks/blob/master/CapStatement/R4capabilitystatement-server.j2
'''
import sys
import os
import os.path
from os import path
import glob
import validators
import fhirclient.r4models.capabilitystatement as CS
import fhirclient.r4models.implementationguide as IG
import fhirclient.r4models.structuredefinition as SD
import fhirclient.r4models.codeableconcept as CC
import fhirclient.r4models.fhirdate as D
import fhirclient.r4models.extension as X
import fhirclient.r4models.contactdetail as CD
import fhirclient.r4models.narrative as N
import fhirclient.r4models.bundle as B
import re
import tarfile
# import fhirclient.r4models.narrative as N
import json
from json import dumps, loads
from requests import post, get
from pathlib import Path
from collections import namedtuple
from pandas import *
from datetime import datetime, date
from stringcase import snakecase, titlecase
from jinja2 import Environment, FileSystemLoader, select_autoescape
from commonmark import commonmark
from lxml import etree
# GLOBALS
fhir_base_url = 'http://hl7.org/fhir/'
f_jurisdiction = CC.CodeableConcept({
"coding": [
{
"system": "urn:iso:std:iso:3166",
"code": "US"
}
]
})
conf_url = 'http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation'
combo_url = 'http://hl7.org/fhir/StructureDefinition/capabilitystatement-search-parameter-combination'
# dict to for SP to get right canonicals, may use spreadsheet or package file in future.
sp_specials = {
'us-core-includeprovenance': 'http://hl7.org/fhir/us/core/SearchParameter/us-core-includeprovenance'}
#this should be a map to the common
sp_common_list = ["address", "address-city", "address-country", "address-postalcode", "address-state", "address-use", "birthdate", "code", "context", "context-quantity", "context-type", "context-type-quantity", "context-type-value", "date", "description", "email", "encounter", "family", "gender", "given", "identifier", "jurisdiction", "medication", "name", "patient", "phone", "phonetic", "publisher", "status", "status", "telecom", "title", "type", "url", "version"]
none_list = ['', ' ', 'none', 'n/a', 'N/A', 'N', 'False']
sep_list = (',', ';', ' ', ', ', '; ')
f_now = D.FHIRDate(str(date.today()))
class bcolors:
HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKCYAN = '\033[96m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
def markdown(text, *args, **kwargs):
return commonmark(text, *args, **kwargs)
def main():
if (len(sys.argv) < 2):
print(
"Error: missing json file - correct usage is:\n\tpython3 R4CapStatement_NarrativeMaker.py [json file] {[Artifacts Folder]}")
return
xls = sys.argv[1]
in_json_file = sys.argv[1]
artifacts_folder = ""
if len(sys.argv) > 2:
artifacts_folder = sys.argv[2]
print('....Generating CapabilityStatement Narrative.....')
with open(in_json_file, 'r') as h:
pjs = json.load(h)
capStatement = CS.CapabilityStatement(pjs)
#print(dumps(capStatement.as_json(), indent=3)) # %% [markdown]
# CapabilityStatement loaded
in_path = ''
in_file = 'R4capabilitystatement-server.j2'
env = Environment(
loader=FileSystemLoader(searchpath=in_path),
autoescape=select_autoescape(['html', 'xml', 'xhtml', 'j2', 'md'])
)
env.filters['markdown'] = markdown
template = env.get_template(in_file)
pname_map = {}
igname_map = {}
csname_map = {}
# Load name maps
if artifacts_folder != "":
print('....Retrieving Artifact Names .....')
artifacts_folder = os.path.abspath(artifacts_folder)
struct_def_files = glob.glob(artifacts_folder + "/StructureDefinition-*.json")
imp_guide_files = glob.glob(artifacts_folder + "/ImplementationGuide-*.json")
cap_stmt_files = glob.glob(artifacts_folder + "/CapabilityStatement-*.json")
pname_map = get_pname_map(struct_def_files)
igname_map = get_igname_map(imp_guide_files)
csname_map = get_csname_map(cap_stmt_files)
# Check access to hl7.org/fhir
r = get (fhir_base_url)
if r.status_code == 200:
print('....Retrieving Online Artifact Names .....')
# Loop through all references in the CapabilityStatement and attempt to retried the artifacts to load the names into the map
# Instantiates
if capStatement.instantiates:
for url in capStatement.instantiates:
if url not in csname_map:
csname_map[url] = get_url_title(url, "instantiates CapabilityStatement")
# Imports
if capStatement.imports:
for url in capStatement.imports:
if url not in csname_map:
csname_map[url] = get_url_title(url, "imports CapabilityStatement")
# Implementation Guides
if capStatement.implementationGuide:
for url in capStatement.implementationGuide:
if url not in igname_map:
igname_map[url] = get_url_title(url, "ImplementationGuide")
# Iterate through rest resources
if capStatement.rest:
for rest in capStatement.rest:
if rest.resource:
for resource in rest.resource:
if resource.profile:
url = resource.profile
if url not in pname_map:
pname_map[url] = get_url_title(url, resource.type + " profile")
if resource.supportedProfile:
for url in resource.supportedProfile:
if url not in pname_map:
pname_map[url] = get_url_title(url, resource.type + " supported profile")
else:
print("Unable to connect to " + fhir_base_url + ". Will not attempt to load online artifacts to retrieve artifact names.")
rendered = template.render(cs=capStatement, path_map='', pname_map=pname_map, purl_map='', sp_map='',
csname_map=csname_map, csurl_map='', sp_url_map='', igname_map=igname_map, igurl_map='')
#template.render(cs=cs, path_map=path_map, pname_map=pname_map, purl_map=purl_map, sp_map=sp_map,
# csname_map=csname_map, csurl_map=csurl_map, igname_map=igname_map, igurl_map=igurl_map)
tempPath = Path.cwd() / "test.html"
tempPath.write_text(rendered)
#print(rendered)
parser = etree.XMLParser(remove_blank_text=True)
root = etree.fromstring(rendered, parser=parser)
div = (etree.tostring(root[1][0], encoding='unicode', method='html'))
print("\n####################################################\n")
#print(etree.tostring(root[1][0], encoding='unicode', method='html'))
print("\n####################################################\n")
narr = N.Narrative()
narr.status = 'generated'
#div = re.sub('https://paciowg.github.io/advance-directives-ig/StructureDefinition-', 'SSSSSSSSSSSSSSSSS', div)
# replace all of the supported profile urls in link text with just the profile name from inside the cononical url
#######div = re.sub('">\(https?://.*/StructureDefinition-(.*)\.html\)', '">\\1', div)
#div = re.sub('">\(https://paciowg.github.io/advance-directives-ig/StructureDefinition-PADI-(PersonalGoal.html)', '\\1', div)
# For some reason
is being replaced with
, which is wrong. Convert it back.
#div = div.replace("
", "
")
#print(div)
narr.div = div
#print(dumps(narr.div, indent=3)) # %% [markdown]
capStatement.text = narr
outfile = 'Narrative-' + in_json_file
path = Path.cwd() / outfile
tempOut = dumps(capStatement.as_json(), indent=4)
tempOut = tempOut.replace("+", "†")
#tempOut = tempOut.replace(“t”, “†”)
#print(tempOut)
path.write_text(tempOut)
print('.............validating..............')
r = validate(capStatement)
if (r.status_code != 200):
print("Error: Unable to validate - status code {}".format(r.status_code))
path = Path.cwd() / 'validation.html'
path.write_text(
f'