What is FHIR?
FHIR (Fast Healthcare Interoperability Resources) is a standard describing data formats, elements and an application programming interface for exchanging electronic health records.
The standard was created by the Health Level Seven International healthcare standards organization,
and it is widely used in healthcare industry.
FHIR has also been implemented by two of the largest EHR (Electronic Health Record) companies in the US: Epic and Cerner.
More data on FHIR can be found here.
Note: Latest FHIR version is 4.0.1,
but many organizations are still working with version 3.0.2 (STU 3).
What is CORTX? CORTX is a distributed object storage system designed for great efficiency, massive capacity, and high HDD-utilization. CORTX is 100% Open Source.
Why this integration important?
As known, hospitals are mostly On-Prem for privacy and security reasons.
Therefore, combining CORTX abilities with hospitals' big data can improve data storage and data analysis solutions for clinical decision making.
During COVID 19 time, those skills have been proven to be missing and the entire healthcare industry working on finding solutions.
can be found here.
Our intergation example code can be found here.
We used a FHIR client to communicate with FHIR based server and boto3 for CORTX S3.
For our generic server we used Flask (a web framework).
Flask documentation can be found here
pip install boto3 fhirclient flask
FHIR have a lot of models, we have only implemented four models. Supported model enum can be found here. To make the code generic as possible we also needed to create mappers:
- get_fhir_model_type: map between a supported model (enum) to fhirclient's model-type
- get_cortx_s3_bucket: map between a supported model (enum) to CORTX S3 bucket name
- revised_mapping: map between a string name of model to supported model (enum).
Those mappers are being used inside our connectors and flask server.
Connector class in an abstract class for handling data manipulations (CRUD - Create, Read, Update, Delete). FhirConnector and CortxConnector have inherited this class and implemented its methods. The possible methods for a model (supported models are in supported_models.py enum as described above) are:
- connect: connect to the server (FHIR server / CORTX S3)
- get: get a model by id
- insert: insert a new model
- update: update a new model
- delete: delete a model by id
- search: search models' data by given search terms
Note: search function won't work on CortxConnector as S3 buckets are read by id only. Note2: Supported search terms (per model) can be found here.
FhirConnector is a class that only requires api_base (FHIR api base url) in its constructor. If the FHIR server also required authentication you should also send app_id, api_key (OAuth2 access key) and secret (OAuth2 secret). After initiating the class call 'connect' method, it will handle authentication if needed.
fhir_connector = FhirConnector('http://test.fhir.org/r3')
fhir_connector.connect()
CortxConnector is a class that requires endpoint_url (CORTX url), aws_access_key_id (AWS access key), aws_secret_access_key (AWS access secret) in its constructor. After initiating the class call 'connect' method, it will handle authentication.
cortx_connector = CortxS3Connector(
endpoint_url='http://uvo19iqqprct5bd9622.vm.cld.sr',
aws_access_key_id='z5_sdXvsSvKKWxjJmvCA9g',
aws_secret_access_key='kUIMmfQCyNzxel8vi8udIsadwuliOYpS/jdyMh8e')
cortx_connector.connect()
Get a patient by id from FHIR, insert it to CORTX.
patient = fhir_connector.get(SupportedModels.PATIENT, '11')
insert_response = cortx_connector.insert(SupportedModels.PATIENT,
patient.as_json())
A quick (1 minute) demo video can be found here. A Full (8 minute) demo video which describe the entire code can be found here.
Flask is a Python web framework used for developing web applications. Flask documentation can be found here
We used this server for enabling an easier way of transferring data from/to both FHIR-based servers and CORTX S3.
We have created a configuration file from which the server reads its configuration. Configuration contains:
- FHIR host (base url of FHIR API server)
- CORTX endpoint url, AWS access key and AWS access secret
We load the configuration by using json.load function.
with open('config.json') as json_data_file:
config = json.load(json_data_file)
As described in the integration walk-through, we initialize our connectors using the configuration.
fhir_connector = FhirConnector(config['fhir']['host'])
fhir_connector.connect()
cortx_connector = CortxS3Connector(
endpoint_url=config['cortx']['endpoint_url'],
aws_access_key_id=config['cortx']['aws_access_key_id'],
aws_secret_access_key=config['cortx']['aws_secret_access_key'])
cortx_connector.connect()
Then, we initialize an app using file name.
app = Flask(__name__)
We used three private methods for our server:
- __get_connector: returns the relevant connector according to the given source ('CORTX','FHIR')
- __get_supported_model: returns supported model (enum) according to the given supported model's name (string)
- __parse_response: parses the response data (on get, search methods) according to the relevant data source ('CORTX','FHIR').
Flask is using 'app.route' decorators for defining urls. 'app' is the Flask instance described in step 2. Example:
@app.route('/<source>/<supported_model_name>/<model_id>', methods=['GET'])
def get_model(source: str, supported_model_name: str, model_id: str):
...
On that example, flask is expecting to get three parameters:
- source ('CORTX','FHIR')
- supported_model_name ('patient','observation','procedure','appointment')
- model_id (model id to get from FHIR/CORTX server)
The 'methods' argument (methods=['GET']) defines the HTTP methods allowed for this function.
All of the methods contains two common parameters:
- source (str): we will use it to get relevant connector and parse data.
- supported_model_name (str): we will use it to get FHIR client's model or CORTX S3 bucket.
Methods:
- get_model - get a model by id
- insert_model - insert a model (by given json on HTTP POST method)
- update_model - update a model (by given json on HTTP PUT method)
- delete_model - delete a model by id
- search_model - search models (by given json-terms on HTTP POST method)
if __name__ == '__main__':
app.run()
And we are up and running :)
Note: We have tested our code mostly on Grahame's test server (r3 [STU 3] - the FHIR most used version today, version 3.0.2). Note 2: Most of the open FHIR servers allow only read (get and search), as they are read-only.
- Add more supported models
- Allow multiple configurations loading to server.py using env files (instead of config.json file)
- Amit Yaniv
- Avi Greenwalds