FHIRPACK Detailed Usage#

The purpose behind this Notebook is to show you some of the posibilities FHIRPACK offers.

Don’t be intimidated, start using what you need and slowly get acquainted with more complex workflows and functionality.

Feel free to refer to our Documentation, our GitLab and GitHub trackers or our Slack channel.

Boilerplate#

[1]:
%load_ext autoreload
%autoreload 2
%pprint

from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"
Pretty printing has been turned OFF

Preamble and Imports#

⚠️ Use only if you haven’t installed FHIR PACK via pip, pipenv or similar in your environment but you have instead cloned the project and are now in the /examples directory

[2]:
import sys,os
sys.path.append(os.getcwd()+'/../src/')
# sys.path

import fhirpack                     as fp
import pandas                       as pd
import fhirpy
import numpy                        as np
import fhir.resources       as fr
import json
from tqdm import tqdm
from fhirpack.constants import CONFIG

Useful Paths for Data Storage#

[3]:
DATAPATH=CONFIG.get('DATAPATH')
RESPATH=f"{DATAPATH}/fhir/"

FHIR Server Connection#

CLI Usage#

[12]:
!fp -o "getPatients" -p "given = Max" #-v
[11]:
!dotenv -f ../.env run python -m fhirpack.cli "-o" "getPatients" -p "given = Max" #-v
                                                   data                                            Patient
0     {'resourceType': 'Patient', 'id': '08674fab965...  08674fab96596e225e0fca7694e8e5cbba3a25ee453e18...
1     {'resourceType': 'Patient', 'id': '53ad3ccbbe6...  53ad3ccbbe6166066be9e216e2b9d352229fa2846b3a9b...
2     {'resourceType': 'Patient', 'id': 'e8567d4db54...  e8567d4db5492014854452992be2b3ce9eeb67b659b66d...
3     {'resourceType': 'Patient', 'id': 'e9b972a5f7d...  e9b972a5f7d398ada6368d5c5622c688291cb7f71a2b4e...
4     {'resourceType': 'Patient', 'id': '2372ec256d3...  2372ec256d326bea633973f156f1a4e9c9076126a9af56...
...                                                 ...                                                ...
9392  {'resourceType': 'Patient', 'id': 'b25c1f9292d...  b25c1f9292da6948ac9718075f20061c9d7aa254734505...
9393  {'resourceType': 'Patient', 'id': '528f19164a6...  528f19164a67e1131b290bd0f4ba02f21be58ff00f96f0...
9394  {'resourceType': 'Patient', 'id': '29a5efae5e9...  29a5efae5e90b1cf94cedd171965fb8c9c6d8b877132c9...
9395  {'resourceType': 'Patient', 'id': 'e8380b48215...  e8380b48215983ea1e7293852b28679857403b81b37b75...
9396  {'resourceType': 'Patient', 'id': 'c2abcc5ca2b...  c2abcc5ca2bbd14d7dc35d832b30f9848211efb4218de4...

[7768 rows x 2 columns]

From Custom Environment File#

[34]:
!cat ../.env.example


SCHEMA=https
PORT=80

#DOMAIN=${SCHEMA}://hapi.fhir.org
#DOMAIN=${SCHEMA}://test.hapifhir.io
#might be unstable or unavailable https://github.com/hapifhir/hapi-fhir/issues/1171#issuecomment-1179728061

DOMAIN=${SCHEMA}://hapi.fhir.org

#DOMAIN=${SCHEMA}://example.com:${PORT}
# only use port if the server is running on a non-standard port
# and this is reflected in the FHIR pagination links
# see https://gitlab.com/fhirpack/main/-/issues/75 for more information

APIBASE=${DOMAIN}/baseR4

#AUTH_METHOD=
#AUTH_PARAMS_PRESET=
#LOGINURL=${DOMAIN}/oauth/token
#OAUTH_TOKEN_ENDPOINT=${DOMAIN}/oauth/token
#OAUTH_USERNAME=username
#OAUTH_PASSWORD=password
#OAUTH_TOKEN=token

DATAPATH=./data

#EXTRACTION_BASE_TOKEN_DICOM=${OAUTH_TOKEN}

[5]:
pack= fp.PACK(envFile='./.env.example')
pack.getPatients(['2918556'])

[5]:
data Patient
0 {'resourceType': 'Patient', 'id': '2918556', '... 2918556
[20]:
!cat .env.example

SCHEMA=https
PORT=80

#DOMAIN=${SCHEMA}://hapi.fhir.org
#DOMAIN=${SCHEMA}://test.hapifhir.io
#might be unstable or unavailable https://github.com/hapifhir/hapi-fhir/issues/1171#issuecomment-1179728061

DOMAIN=${SCHEMA}://hapi.fhir.org

#DOMAIN=${SCHEMA}://example.com:${PORT}
# only use port if the server is running on a non-standard port
# and this is reflected in the FHIR pagination links
# see https://gitlab.com/fhirpack/main/-/issues/75 for more information

APIBASE=${DOMAIN}/baseR4

#AUTH_METHOD=
#AUTH_PARAMS_PRESET=
#LOGINURL=${DOMAIN}/oauth/token
#OAUTH_TOKEN_ENDPOINT=${DOMAIN}/oauth/token
#OAUTH_USERNAME=username
#OAUTH_PASSWORD=password
#OAUTH_TOKEN=token

LOGSPATH=./logs
DATAPATH=./data

#EXTRACTION_BASE_TOKEN_DICOM=${OAUTH_TOKEN}

[7]:
pack= fp.PACK(envFile='./.env.example')

for i,e in pack.getPatients(['2918556']).data.items():
    print(
            json.dumps(e.serialize(), indent=4, sort_keys=True)[:200],
            '\n...'
    )
# pack.getPatients(['2918556'])

{
    "active": false,
    "address": [
        {
            "city": "[Ljava.lang.String;@6486517a",
            "country": "lJltcNpfGu",
            "district": "kIATSJDTIN",
            "line": [

...

Manual Setup#

[18]:
pack  = fp.PACK("http://hapi.fhir.org/baseR4")

# Another less reliable alternative server to test things:
# pack  = fp.PACK("http://test.fhir.org/r4/")

pack.getPatients(['2918556'])

[18]:
data Patient
0 {'resourceType': 'Patient', 'id': '2918556', '... 2918556
[24]:
pack.getPatients(['2918556']).data

[24]:
0    {'resourceType': 'Patient', 'id': '2918556', '...
Name: data, dtype: object
[25]:
pack.getPatients(['2918556']).iloc[0]

[25]:
data       {'resourceType': 'Patient', 'id': '2918556', '...
Patient                                              2918556
Name: 0, dtype: object
[31]:
# The data column always contains the raw fhirpy.lib.SyncFHIRResource objects
# that can be used according to their API.

myResource = pack.getPatients(['2918556']).data.iloc[0]
myResource.serialize()

[31]:
{'resourceType': 'Patient', 'id': '2918556', 'meta': {'versionId': '1', 'lastUpdated': '2022-04-28T09:14:12.407+00:00', 'source': '#XvmdJVuEr4W9S9I5'}, 'text': {'status': 'generated', 'div': '<div xmlns="http://www.w3.org/1999/xhtml"><div class="hapiHeaderText">muagd nrxrykl <b>KMLNNPEUVZ </b> zttwnr </div><table class="hapiPropertyTable"><tbody><tr><td>Identifier</td><td>LiwdDQHoIt</td></tr><tr><td>Address</td><td><span>[Ljava.lang.String;@74a2d899 </span><br/><span>[Ljava.lang.String;@6486517a </span><span>QHmEoccROR </span><span>lJltcNpfGu </span></td></tr><tr><td>Date of birth</td><td><span>23 April 1863</span></td></tr></tbody></table></div>'}, 'identifier': [{'use': 'official', 'type': {'coding': [{'system': 'https://wfmvwfsriv.com', 'version': 'TEGlN', 'code': 'secondary', 'display': 'VUxeSKGrSXyWRiWttgtn', 'userSelected': True}], 'text': 'ZrsDqCrqYCCqEHOIdtKkwHTusypwlhoxVRAqMWEhBPoerfPRkmMeFUJKPcnlKfHAfVmpWWTMXEClcsEQZWDqqBKQvajndOvYYjiG'}, 'system': 'https://ceyyockogw.com', 'value': 'LiwdDQHoIt', 'period': {'start': '1892-04-01', 'end': '1988-12-08'}, 'assigner': {'reference': 'SseKXOELXg', 'type': 'https://dldmpzerig.com', 'display': 'XlkPcdGvbizOJCM'}}], 'active': False, 'name': [{'use': 'old', 'text': 'MIfctqm', 'family': 'KMLnNPeuVz', 'given': ['nrxrykl'], 'prefix': ['muagd'], 'suffix': ['zttwnr'], 'period': {'start': '1940-10-30', 'end': '1969-07-27'}}], 'telecom': [{'system': 'pager', 'value': '2913897131', 'use': 'home', 'rank': 6, 'period': {'start': '1928-10-09', 'end': '1821-01-22'}}], 'gender': 'male', 'birthDate': '1863-04-23', 'deceasedDateTime': '1873-04-04T09:14:09.404316500Z', 'address': [{'use': 'old', 'type': 'postal', 'text': 'qExcvDSAJoeSvQnbxFzG', 'line': ['[Ljava.lang.String;@74a2d899'], 'city': '[Ljava.lang.String;@6486517a', 'district': 'kIATSJDTIN', 'state': 'QHmEoccROR', 'postalCode': 'pTGzgOEOYm', 'country': 'lJltcNpfGu', 'period': {'start': '1877-04-15', 'end': '1864-11-08'}}]}

For Docker Enthusiasts#

[32]:
# alternatively, if you're using our Dockerfile to run your FHIR server
# remember to run "docker-compose up" in your local clone of FHIRPACK first
# run "docker-compose up" in your local clone of FHIRPACK first

# pack  = fp.PACK("http://127.0.0.1:42112/hapi-fhir-jpaserver/fhir")
# pack  = fp.PACK(envFile="../../docker-compose.env")

# pack.getPatients(['1']).data.iloc[0].serialize()

# pack.getPatients(['146053'])

Configuring FHIRPACK from the Default Environment File (./.env)#

[22]:
pack = fp.PACK()
# pack.client.authorization

A Motivating Example#

[23]:
# for patients whose lastname is Koepp, find the root identity,
# get their associated diagnostic reports and report their ID and status

pack.getPatients(searchParams={"family":"koepp"})\
    .getRootPatients()\
            .explode()\
                    .getDiagnosticReports()\
                            .dropna()\
                                    .gatherSimplePaths(['id','status'])\
                                            .explode(['id','status'])
SEARCH[Patient]> : 100%|██████████| 13/13 [00:00<00:00, 18.66it/s]
SEARCH[DiagnosticReport]> : 100%|██████████| 12/12 [00:00<00:00, 28.99it/s]
SEARCH[DiagnosticReport]> : 100%|██████████| 13/13 [00:00<00:00, 30.77it/s]
SEARCH[DiagnosticReport]> : 100%|██████████| 9/9 [00:00<00:00, 25.92it/s]
SEARCH[DiagnosticReport]> : 100%|██████████| 9/9 [00:00<00:00, 25.51it/s]
SEARCH[DiagnosticReport]> : 100%|██████████| 20/20 [00:00<00:00, 36.14it/s]
SEARCH[DiagnosticReport]> : 100%|██████████| 5/5 [00:00<00:00, 16.15it/s]
SEARCH[DiagnosticReport]> : 100%|██████████| 1/1 [00:00<00:00,  4.17it/s]
[23]:
id status
0 None None
1 None None
2 6570895b-5943-430b-8f7b-e5df1ffc6d83 final
2 85d08e59-3e58-4518-be8b-3ccfc15cc9fa final
2 35f7efea-6557-467e-a693-34d73a097f64 final
... ... ...
8 None None
9 None None
10 None None
11 None None
12 1516127 final

75 rows × 2 columns

FHIRPACK and FHIR Resources#

References#

[24]:
patientReference = pack.getReferences(['Patient/647487'])

patientReference.data.iloc[0].to_resource().serialize()

patientReference.getResources()
[24]:
{'resourceType': 'Patient',
 'id': '647487',
 'meta': {'versionId': '1',
  'lastUpdated': '2020-03-20T21:38:58.974+00:00',
  'source': '#bE19PhJ3nQMhB1xy'},
 'text': {'status': 'generated',
  'div': '<div xmlns="http://www.w3.org/1999/xhtml"><div class="hapiHeaderText">Series_Status: Not complete Evaluation_Status_1.0: Valid Evaluation_Status_2.0: Valid Evaluation_Status_3.0: Valid Evaluation_Status_4.0: Valid Evaluation_Status_5.0: Valid <b># 5 PENTACEL AT ≥ 4 YRS </b></div><table class="hapiPropertyTable"><tbody><tr><td>Date of birth</td><td><span>20 January 2016</span></td></tr></tbody></table></div>'},
 'name': [{'family': '# 5 Pentacel at ≥ 4 yrs',
   'given': ['Series_Status: Not complete',
    'Evaluation_Status_1.0: Valid',
    'Evaluation_Status_2.0: Valid',
    'Evaluation_Status_3.0: Valid',
    'Evaluation_Status_4.0: Valid',
    'Evaluation_Status_5.0: Valid']}],
 'gender': 'female',
 'birthDate': '2016-01-20'}
GET[None]> : 100%|██████████| 1/1 [00:00<00:00,  4.17it/s]
[24]:
data
0 {'resourceType': 'Patient', 'id': '647487', 'm...

Resources#

Reference to Resource#

[25]:
multiPatientReference = pack.getReferences(
    [
        'Patient/1849165',
        'Patient/647487'
    ])

multiPatientReference.data.apply( lambda x:x.id )
[25]:
0    1849165
1     647487
Name: data, dtype: object

Direct Resource Access#

[26]:
pack.getPatients(searchParams={"family":"Betterhalf"})
SEARCH[Patient]> : 100%|██████████| 12/12 [00:00<00:00, 16.03it/s]
[26]:
data
0 {'resourceType': 'Patient', 'id': '2080537', '...
1 {'resourceType': 'Patient', 'id': '2080822', '...
2 {'resourceType': 'Patient', 'id': '2153287', '...
3 {'resourceType': 'Patient', 'id': '619088', 'm...
4 {'resourceType': 'Patient', 'id': '2548478', '...
5 {'resourceType': 'Patient', 'id': '2548794', '...
6 {'resourceType': 'Patient', 'id': '2737336', '...
7 {'resourceType': 'Patient', 'id': '1376090', '...
8 {'resourceType': 'Patient', 'id': '1376092', '...
9 {'resourceType': 'Patient', 'id': '1377059', '...
10 {'resourceType': 'Patient', 'id': '1377093', '...
11 {'resourceType': 'Patient', 'id': '1388633', '...
[27]:
pack.getPatients(searchParams={}).size
SEARCH[Patient]> :   0%|          | 100/185636 [00:01<56:30, 54.72it/s]
[27]:
100
[30]:
multiPatientResource = pack.getResources(
    ['Patient/1849165',
     'Patient/647487']
)

multiPatientResource
GET[None]> : 100%|██████████| 2/2 [00:00<00:00,  4.36it/s]
[30]:
data
0 {'resourceType': 'Patient', 'id': '1849165', '...
1 {'resourceType': 'Patient', 'id': '647487', 'm...

Serialization and Element Access#

[31]:
patientResource = patientReference.data.iloc[0].to_resource()

patientResource['name'][0]['given']

patientResource.serialize().get('birthDate')
[31]:
['Series_Status: Not complete',
 'Evaluation_Status_1.0: Valid',
 'Evaluation_Status_2.0: Valid',
 'Evaluation_Status_3.0: Valid',
 'Evaluation_Status_4.0: Valid',
 'Evaluation_Status_5.0: Valid']
[31]:
'2016-01-20'
[32]:
multiPatientReference.getResources()
GET[None]> : 100%|██████████| 2/2 [00:00<00:00,  4.47it/s]
[32]:
data
0 {'resourceType': 'Patient', 'id': '1849165', '...
1 {'resourceType': 'Patient', 'id': '647487', 'm...
[33]:
multiPatientResource[:1].pretty
{
    "_birthDate": {
        "extension": [
            {
                "url": "http://hl7.org/fhir/StructureDefinition/patient-birthTime",
                "valueDateTime": "1974-12-25T14:35:45-05:00"
            }
        ]
    },
    "active": true,
    "address": [
        {
            "city": "PleasantVille",
            "district": "Rainbow",
            "line": [
                "534 Erewhon St"
            ],
            "period": {
                "start": "1974-12-25"
            },
            "postalCode": "3999",
            "state": "Vic",
            "text": "534 Erewhon St PeasantVille, Rainbow, Vic  3999",
            "type": "both",
            "use": "home"
        }
    ],
    "birthDate": "1974-12-25",
    "contact": [
        {
            "address": {
                "city": "PleasantVille",
                "district": "Rainbow",
                "line": [
                    "534 Erewhon St"
                ],
                "period": {
                    "start": "1974-12-25"
                },
                "postalCode": "3999",
                "state": "Vic",
                "type": "both",
                "use": "home"
            },
            "gender": "female",
            "name": {
                "_family": {
                    "extension": [
                        {
                            "url": "http://hl7.org/fhir/StructureDefinition/humanname-own-prefix",
                            "valueString": "VV"
                        }
                    ]
                },
                "family": "du March\u00e9",
                "given": [
                    "B\u00e9n\u00e9dicte"
                ]
            },
            "period": {
                "start": "2012"
            },
            "relationship": [
                {
                    "coding": [
                        {
                            "code": "N",
                            "system": "http://terminology.hl7.org/CodeSystem/v2-0131"
                        }
                    ]
                }
            ],
            "telecom": [
                {
                    "system": "phone",
                    "value": "+33 (237) 998327"
                }
            ]
        }
    ],
    "deceasedBoolean": false,
    "gender": "male",
    "id": "1849165",
    "identifier": [
        {
            "assigner": {
                "display": "Acme Healthcare"
            },
            "period": {
                "start": "2001-05-06"
            },
            "system": "urn:oid:1.2.36.146.595.217.0.1",
            "type": {
                "coding": [
                    {
                        "code": "MR",
                        "system": "http://terminology.hl7.org/CodeSystem/v2-0203"
                    }
                ]
            },
            "use": "usual",
            "value": "12345"
        }
    ],
    "managingOrganization": {
        "reference": "Organization/1845391"
    },
    "meta": {
        "lastUpdated": "2021-02-08T21:42:25.829+00:00",
        "source": "#PBPO58xnJNERsS1A",
        "versionId": "1"
    },
    "name": [
        {
            "family": "Chalmers",
            "given": [
                "Peter",
                "James"
            ],
            "use": "official"
        },
        {
            "given": [
                "Jim"
            ],
            "use": "usual"
        },
        {
            "family": "Windsor",
            "given": [
                "Peter",
                "James"
            ],
            "period": {
                "end": "2002"
            },
            "use": "maiden"
        }
    ],
    "resourceType": "Patient",
    "telecom": [
        {
            "use": "home"
        },
        {
            "rank": 1,
            "system": "phone",
            "use": "work",
            "value": "(03) 5555 6473"
        },
        {
            "rank": 2,
            "system": "phone",
            "use": "mobile",
            "value": "(03) 3410 5613"
        },
        {
            "period": {
                "end": "2014"
            },
            "system": "phone",
            "use": "old",
            "value": "(03) 5555 8834"
        }
    ],
    "text": {
        "div": "<div xmlns=\"http://www.w3.org/1999/xhtml\">\n\t\t\t<table>\n\t\t\t\t<tbody>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>Name</td>\n\t\t\t\t\t\t<td>Peter James \n              <b>Chalmers</b> (&quot;Jim&quot;)\n            </td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>Address</td>\n\t\t\t\t\t\t<td>534 Erewhon, Pleasantville, Vic, 3999</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>Contacts</td>\n\t\t\t\t\t\t<td>Home: unknown. Work: (03) 5555 6473</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>Id</td>\n\t\t\t\t\t\t<td>MRN: 12345 (Acme Healthcare)</td>\n\t\t\t\t\t</tr>\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>",
        "status": "generated"
    }
}
[34]:
fp.PACK().getDiagnosticReports(
    ['Patient/647487']
)[:1].keys

GET[DiagnosticReport]> : 100%|██████████| 1/1 [00:00<00:00,  4.40it/s]
resourceType
id
meta
meta.versionId
meta.lastUpdated
meta.source
text
text.status
text.div
name
name.family
name.given
gender
birthDate

PANDAS-Style DataFrame Operations#

[35]:
multiPatientReference[:1]

multiPatientReference[-1:]

multiPatientReference.values

multiPatientReference.info()
[35]:
data
0 {'reference': 'Patient/1849165'}
[35]:
data
1 {'reference': 'Patient/647487'}
[35]:
array([[<SyncFHIRReference Patient/1849165>],
       [<SyncFHIRReference Patient/647487>]], dtype=object)
<class 'fhirpack.base.Frame'>
RangeIndex: 2 entries, 0 to 1
Data columns (total 1 columns):
 #   Column  Non-Null Count  Dtype
---  ------  --------------  -----
 0   data    2 non-null      object
dtypes: object(1)
memory usage: 144.0+ bytes

fhirpack.base.Frame#

[36]:
fp.base.Frame( [[patientResource]], columns=['data'])
[36]:
data
0 {'resourceType': 'Patient', 'id': '647487', 'm...

Extraction#

Patients#

FHIR Patient Resource

[37]:
pack.getPatients(
    [
            '647487'
    ]
)

GET[Patient]> : 100%|██████████| 1/1 [00:00<00:00,  4.38it/s]
[37]:
data
0 {'resourceType': 'Patient', 'id': '647487', 'm...
[38]:
pack.getPatients(
    [
            '647487'
    ]
).data.item().serialize()
GET[Patient]> : 100%|██████████| 1/1 [00:00<00:00,  4.32it/s]
[38]:
{'resourceType': 'Patient',
 'id': '647487',
 'meta': {'versionId': '1',
  'lastUpdated': '2020-03-20T21:38:58.974+00:00',
  'source': '#bE19PhJ3nQMhB1xy'},
 'text': {'status': 'generated',
  'div': '<div xmlns="http://www.w3.org/1999/xhtml"><div class="hapiHeaderText">Series_Status: Not complete Evaluation_Status_1.0: Valid Evaluation_Status_2.0: Valid Evaluation_Status_3.0: Valid Evaluation_Status_4.0: Valid Evaluation_Status_5.0: Valid <b># 5 PENTACEL AT ≥ 4 YRS </b></div><table class="hapiPropertyTable"><tbody><tr><td>Date of birth</td><td><span>20 January 2016</span></td></tr></tbody></table></div>'},
 'name': [{'family': '# 5 Pentacel at ≥ 4 yrs',
   'given': ['Series_Status: Not complete',
    'Evaluation_Status_1.0: Valid',
    'Evaluation_Status_2.0: Valid',
    'Evaluation_Status_3.0: Valid',
    'Evaluation_Status_4.0: Valid',
    'Evaluation_Status_5.0: Valid']}],
 'gender': 'female',
 'birthDate': '2016-01-20'}
[39]:
for i,e in pack.getPatients(['647487','1849165']).itertuples():
    print (f"Row {i} with Patient ID {e.id}")
GET[Patient]> : 100%|██████████| 2/2 [00:00<00:00,  4.40it/s]
Row 0 with Patient ID 647487
Row 1 with Patient ID 1849165

[40]:
getPatientsAsList=pack.getPatients(['647487','1849165']).cast('list')

getPatientsAsList
GET[Patient]> : 100%|██████████| 2/2 [00:00<00:00,  4.42it/s]
[40]:
[[<SyncFHIRResource Patient/647487>], [<SyncFHIRResource Patient/1849165>]]
[41]:
[e.pop().id for e in getPatientsAsList]
[41]:
['647487', '1849165']

Patients as Operand and other Resources#

[42]:
patients = pack.getPatients(
    ['9ac622c4-61c8-40b4-b6d9-06cfeb3d6995',
    '1849165',
    '649231',
    '650224']
)

GET[Patient]> : 100%|██████████| 4/4 [00:00<00:00,  4.38it/s]

Root Patients and Identities#

[43]:
pack.getRootPatients(
    [
        'b558da74-7756-4845-87d5-d1cca8b79a62',
    ],
).explode().gatherSimplePaths(["id"]).id.unique()
SEARCH[Patient]> : 100%|██████████| 1/1 [00:00<00:00,  4.30it/s]
[43]:
array(['9ac622c4-61c8-40b4-b6d9-06cfeb3d6995'], dtype=object)
[44]:
patients.getRootPatients()
[44]:
data
0 [{'resourceType': 'Patient', 'id': '9ac622c4-6...
1 [{'resourceType': 'Patient', 'id': '1849165', ...
2 [{'resourceType': 'Patient', 'id': '649231', '...
3 [{'resourceType': 'Patient', 'id': '650224', '...
[45]:
pack.getResources(
    [
            'Patient/b558da74-7756-4845-87d5-d1cca8b79a62'
    ]
).getRootPatients()
GET[None]> : 100%|██████████| 1/1 [00:00<00:00,  4.59it/s]
SEARCH[Patient]> : 100%|██████████| 1/1 [00:00<00:00,  4.41it/s]
[45]:
data
0 [{'resourceType': 'Patient', 'id': '9ac622c4-6...
[46]:
rootPatients = patients.getResources().getRootPatients().explode()

rootPatients.gatherSimplePaths(['id','birthDate'])
GET[None]> : 100%|██████████| 4/4 [00:00<00:00, 48629.61it/s]
[46]:
id birthDate
0 9ac622c4-61c8-40b4-b6d9-06cfeb3d6995 1981-01-01
1 1849165 1974-12-25
2 649231 2018-12-21
3 650224 2007-02-08
[47]:
rootPatients.data[0].name
[47]:
[{'family': 'TestFamily', 'given': ['TestGiven']}]
[48]:
for link in rootPatients.data[0]['link']:
    lpat=link.other.to_resource()
    print(lpat.id)
b558da74-7756-4845-87d5-d1cca8b79a62

Linked Patients#

[49]:
pack.getLinkedPatients(
    [
            '9ac622c4-61c8-40b4-b6d9-06cfeb3d6995',
    ]
)
GET[Patient]> : 100%|██████████| 1/1 [00:00<00:00,  3.64it/s]
[49]:
data
0 [{'resourceType': 'Patient', 'id': 'b558da74-7...

Conditions#

FHIR Condition Resource

[50]:
conditions = pack.getConditions(
    searchParams={
            'code':'44465007',
            '_sort': '-onset-date',
    }
)[:5]

conditions
SEARCH[Condition]> : 100%|██████████| 36/36 [00:00<00:00, 90.74it/s]
[50]:
data
0 {'resourceType': 'Condition', 'id': '1584692',...
1 {'resourceType': 'Condition', 'id': '1591180',...
2 {'resourceType': 'Condition', 'id': '1402386',...
3 {'resourceType': 'Condition', 'id': '1403562',...
4 {'resourceType': 'Condition', 'id': '1495528',...

Patients for Conditions#

[51]:
conditions.getPatients().explode().gatherSimplePaths(['id','name.given','name.family'])

SEARCH[Patient]> : 100%|██████████| 1/1 [00:00<00:00,  4.46it/s]
SEARCH[Patient]> : 100%|██████████| 1/1 [00:00<00:00,  4.27it/s]
SEARCH[Patient]> : 100%|██████████| 1/1 [00:00<00:00,  4.31it/s]
SEARCH[Patient]> : 100%|██████████| 1/1 [00:00<00:00,  4.44it/s]
SEARCH[Patient]> : 100%|██████████| 1/1 [00:00<00:00,  4.28it/s]
[51]:
id name.given name.family
0 1583909 [[Shayne60]] [Miller503]
1 1590885 [[Ty725]] [Sporer811]
2 1402076 [[Phyliss]] [Nyambura]
3 1403421 [[Stefania]] [Bethwell]
4 1495412 [[Hallie315], [Hallie315]] [Donnelly343, Osinski784]
[52]:
conditions\
    .getPatients()\
            .explode()\
                    .data.apply(lambda x:x.id)\
                            .to_csv(f"data.ignore")
SEARCH[Patient]> : 100%|██████████| 1/1 [00:00<00:00,  4.38it/s]
SEARCH[Patient]> : 100%|██████████| 1/1 [00:00<00:00,  4.41it/s]
SEARCH[Patient]> : 100%|██████████| 1/1 [00:00<00:00,  4.39it/s]
SEARCH[Patient]> : 100%|██████████| 1/1 [00:00<00:00,  4.47it/s]
SEARCH[Patient]> : 100%|██████████| 1/1 [00:00<00:00,  4.37it/s]
[53]:
!cat data.ignore
,data
0,1583909
1,1590885
2,1402076
3,1403421
4,1495412
[54]:
conditions = pd.read_csv( f"data.ignore", index_col=0)
conditions.shape
conditions
[54]:
(5, 1)
[54]:
data
0 1583909
1 1590885
2 1402076
3 1403421
4 1495412

Conditions for Patient#

[55]:
pack.getPatients(['8cbf1128-3644-47a1-9cc8-05f1aac6071d']).getConditions().explode()
GET[Patient]> : 100%|██████████| 1/1 [00:00<00:00,  4.18it/s]
SEARCH[Condition]> : 100%|██████████| 5/5 [00:00<00:00, 20.75it/s]
[55]:
data
0 {'resourceType': 'Condition', 'id': '2b7a4d69-...
0 {'resourceType': 'Condition', 'id': '0fc73e7c-...
0 {'resourceType': 'Condition', 'id': 'cb1bab6c-...
0 {'resourceType': 'Condition', 'id': 'bc301bbf-...
0 {'resourceType': 'Condition', 'id': '5db4866a-...

Observations#

FHIR Observation Resource

[ ]:
pack\
    .getPatients(['Patient/258974'])\
            .getConditions(searchParams={"code":"44465007"})\
                    .explode()\
                            .getPatients(searchParams={"family":"Keebler762"})
GET[Patient]> : 100%|██████████| 1/1 [00:00<00:00,  4.42it/s]
SEARCH[Condition]> : 100%|██████████| 1/1 [00:00<00:00,  4.40it/s]
SEARCH[Patient]> : 100%|██████████| 1/1 [00:00<00:00,  4.37it/s]
data
0 [{'resourceType': 'Patient', 'id': '258974', '...
[ ]:

from datetime import datetime interesting = pack.getPatients(['Patient/2164033']).\ getObservations( searchParams={ "code":"http ://loinc.org|29463-7", } )\ .gatherSimplePaths(['id','issued','code.coding.code'])\ .explode(['id','issued','code.coding.code']) interesting.issued = pd.to_datetime(interesting.issued).dropna().apply(lambda x:x.replace(tzinfo=None)) interesting[interesting.issued> datetime.fromisoformat('2021-01-01T00:00:00')]
GET[Patient]> : 100%|██████████| 1/1 [00:00<00:00,  3.95it/s]
SEARCH[Observation]> : 100%|██████████| 1/1 [00:00<00:00,  4.23it/s]
id issued code.coding.code
0 2164039 2021-06-05 05:30:23.291 [29463-7]

Episodes of Care#

FHIR EpisodeOfCare Resource

[56]:
rootPatients[:2]
[56]:
data
0 {'resourceType': 'Patient', 'id': '9ac622c4-61...
1 {'resourceType': 'Patient', 'id': '1849165', '...
[57]:
pack.getPatients(['P0522-patientBSJ1']).getEpisodesOfCare()
GET[Patient]> : 100%|██████████| 1/1 [00:00<00:00,  4.41it/s]
SEARCH[EpisodeOfCare]> : 100%|██████████| 1/1 [00:00<00:00,  4.37it/s]
[57]:
data
0 [{'resourceType': 'EpisodeOfCare', 'id': 'P052...

Family Member Histories#

FHIR FamilyMemberHistory Resource

[58]:
pack.getPatients(['Patient/2866670']).getFamilyMemberHistories()
GET[Patient]> : 100%|██████████| 1/1 [00:00<00:00,  4.09it/s]
SEARCH[FamilyMemberHistory]> : 100%|██████████| 1/1 [00:00<00:00,  4.49it/s]
[58]:
data
0 [{'resourceType': 'FamilyMemberHistory', 'id':...

Medication Administrations#

FHIR MedicationAdministration Resource

[59]:
pack.getPatients(['Patient/31678']).getMedicationAdministrations()[:5]
GET[Patient]> : 100%|██████████| 1/1 [00:00<00:00,  4.33it/s]
SEARCH[MedicationAdministration]> : 100%|██████████| 1/1 [00:00<00:00,  4.50it/s]
[59]:
data
0 [{'resourceType': 'MedicationAdministration', ...

Medication Requests#

FHIR MedicationRequest Resource

[60]:
pack.getPatients(['Patient/202205uscore-patient-example-1']).getMedicationRequests().explode()
GET[Patient]> : 100%|██████████| 1/1 [00:00<00:00,  3.93it/s]
SEARCH[MedicationRequest]> : 100%|██████████| 3/3 [00:00<00:00, 12.37it/s]
[60]:
data
0 {'resourceType': 'MedicationRequest', 'id': '2...
0 {'resourceType': 'MedicationRequest', 'id': '2...
0 {'resourceType': 'MedicationRequest', 'id': '2...

Diagnostic Reports#

FHIR DiagnosticReport Resource

[63]:
diagnosticReports=pack\
    .getPatients(["Patient/9b8c1901-62ee-48a7-8229-b771d59f1e5f"])\
            .getDiagnosticReports(
                    searchParams={}
                    )\
                    .explode()

diagnosticReports
GET[Patient]> : 100%|██████████| 1/1 [00:00<00:00,  4.15it/s]
SEARCH[DiagnosticReport]> : 100%|██████████| 12/12 [00:00<00:00, 27.91it/s]
[63]:
data
0 {'resourceType': 'DiagnosticReport', 'id': '12...
0 {'resourceType': 'DiagnosticReport', 'id': '5a...
0 {'resourceType': 'DiagnosticReport', 'id': '2d...
0 {'resourceType': 'DiagnosticReport', 'id': '01...
0 {'resourceType': 'DiagnosticReport', 'id': '47...
0 {'resourceType': 'DiagnosticReport', 'id': 'b1...
0 {'resourceType': 'DiagnosticReport', 'id': '58...
0 {'resourceType': 'DiagnosticReport', 'id': '87...
0 {'resourceType': 'DiagnosticReport', 'id': '8c...
0 {'resourceType': 'DiagnosticReport', 'id': 'ac...
0 {'resourceType': 'DiagnosticReport', 'id': 'bc...
0 {'resourceType': 'DiagnosticReport', 'id': 'bf...
[64]:
paths=[
    "subject",
    "presentedForm.contentType",
    "presentedForm.data",
    "presentedForm.url",
    "presentedForm.title",
    "presentedForm.creation"
]



diagnosticReports=pack.getDiagnosticReports(
    searchParams={
            "_id":"19bdf90a-8ca4-4921-8aeb-2bf3423aaf09",
            # "identifier":"|",
            # "_content":"covid19",
            # "code":"|",
            # "issued__gt":"2010-01-01",
            # "issued__lt":"2011-01-01"
    }
)

diagnosticReports.gatherSimplePaths(paths)
SEARCH[DiagnosticReport]> : 100%|██████████| 1/1 [00:00<00:00,  4.10it/s]
[64]:
subject presentedForm.contentType presentedForm.data presentedForm.url presentedForm.title presentedForm.creation
0 {'reference': 'Patient/e92abcdc-a300-41e0-aa0f... [text/plain] [Q2pJd01UQXRNVEV0TVRNS0NpTWdRMmhwWldZZ1EyOXRjR... None None None

Downloading URLs Found in Resources#

[65]:
paths=[
    'id',
    'subject.reference',
    'presentedForm.url'
]

diagnosticReports = pack.getPatients(
    ['Patient/e92abcdc-a300-41e0-aa0f-378daebe25dc']
    )\
        .getDiagnosticReports()[:10]

diagnosticReports=diagnosticReports.gatherSimplePaths(paths)
diagnosticReports=diagnosticReports.explode('presentedForm.url')
diagnosticReports

# diagnosticReports['path']=diagnosticReports['id']+'_'+diagnosticReports['presentedForm.url'].str.split('/').str[-1:].str[0]
# diagnosticReports['data']=diagnosticReports.getURLBytes(operateOnCol='presentedForm.url')
# diagnosticReports.sendBytesToFile()
GET[Patient]> : 100%|██████████| 1/1 [00:00<00:00,  4.37it/s]
SEARCH[DiagnosticReport]> : 100%|██████████| 11/11 [00:00<00:00, 23.44it/s]
[65]:
id subject.reference presentedForm.url
0 [1ca4c411-116d-49d7-82e0-cde6bff90cfd, 126df18... [Patient/e92abcdc-a300-41e0-aa0f-378daebe25dc,... None
[66]:
diagnosticReports.loc[:, diagnosticReports.columns!='data'][:5]
[66]:
id subject.reference presentedForm.url
0 [1ca4c411-116d-49d7-82e0-cde6bff90cfd, 126df18... [Patient/e92abcdc-a300-41e0-aa0f-378daebe25dc,... None

Imaging Studies#

FHIR ImagingStudy Resource

[75]:
conds=pack.getConditions(
    searchParams={
            "code":"C61",
            # "_content": "leber"
    }
    )
conds
SEARCH[Condition]> : 100%|██████████| 1/1 [00:00<00:00,  4.55it/s]
[75]:
data
0 {'resourceType': 'Condition', 'id': '1849657',...
[76]:
pack\
    .getImagingStudies(searchParams={"endpoint:missing":False})\
            .gatherSimplePaths(['series.description','series.uid','identifier.value'])

# use .getDICOMInstances().sendDICOMToFiles() to download DICOM files
SEARCH[ImagingStudy]> : 100%|██████████| 4/4 [00:00<00:00, 14.80it/s]
[76]:
series.description series.uid identifier.value
0 None [IdType-2] [Identifier-117]
1 None [IdType-5] [Identifier-118]
2 None None None
3 None [1.2.276.0.7230010.3.1.3.8323329.10.1594989066... [urn:oid:1.2.826.0.1.3680043.8.498.94636677472...

Transformation#

Gathering Fields from Resources#

JSON-Serialized Resource Keys#

[77]:
patients.gatherKeys().data.explode().value_counts()[:10]
[77]:
name.given        6
name.family       5
telecom.use       5
resourceType      4
id                4
telecom.system    4
gender            4
name              4
birthDate         4
telecom.value     4
Name: data, dtype: int64
[78]:
pack.getPatients(
    ['e92abcdc-a300-41e0-aa0f-378daebe25dc']
    )\
        .gatherKeys(['valueString'])\
            .explode()
GET[Patient]> : 100%|██████████| 1/1 [00:00<00:00,  4.40it/s]
[78]:
data
0 resourceType
0 id
0 meta
0 meta.versionId
0 meta.lastUpdated
... ...
0 communication.language.coding
0 communication.language.coding.system
0 communication.language.coding.code
0 communication.language.coding.display
0 communication.language.text

118 rows × 1 columns

JSON-Serialized Resource Values#

[79]:
pack.getPatients(
    ['e92abcdc-a300-41e0-aa0f-378daebe25dc'],
    # includeLinkedPatients=True
    )\
        .gatherValuesForKeys(['valueString'])\
            .explode()
GET[Patient]> : 100%|██████████| 1/1 [00:00<00:00,  4.36it/s]
[79]:
data
0 White
0 Not Hispanic or Latino
0 Nanci249 Gleichner915

Nested FHIR References#

[80]:
pack.gatherReferences(
    ['Patient/9ac622c4-61c8-40b4-b6d9-06cfeb3d6995']
    , recursive=True
)
[80]:
referencer referencee
0 {'reference': 'Patient/9ac622c4-61c8-40b4-b6d9... [Patient/b558da74-7756-4845-87d5-d1cca8b79a62]
1 {'reference': 'Patient/b558da74-7756-4845-87d5... []

Nested Text and Narratives#

[81]:
fp.PACK().getPatients(
    ['Patient/1555106'],
    )\
        .getObservations()[:5]\
            .explode()\
                .gatherText()
GET[Patient]> : 100%|██████████| 1/1 [00:00<00:00,  4.15it/s]
SEARCH[Observation]> :  15%|█▍        | 100/677 [00:00<00:02, 266.45it/s]
[81]:
data
0 [laboratory, Alkaline phosphatase [Enzymatic a...
1 [laboratory, 7.5804, Protein [Mass/volume] in ...
2 [laboratory, Glomerular filtration rate/1.73 s...
3 [laboratory, MCH [Entitic mass] by Automated c...
4 [laboratory, 20.88, Carbon dioxide, total [Mol...
... ...
95 [Diastolic Blood Pressure, 138, Blood Pressure...
96 [laboratory, Eosinophils/100 leukocytes in Blo...
97 [8.0379, laboratory, C reactive protein [Mass/...
98 [laboratory, Monocytes/100 leukocytes in Blood...
99 [laboratory, 35.401, Creatine kinase [Enzymati...

100 rows × 1 columns

Dates#

[82]:
fp.PACK().getPatients(
    ['Patient/1555106'],
    )\
        .getObservations()[:5]\
            .explode()\
                .gatherValuesForKeys(['issued'])
GET[Patient]> : 100%|██████████| 1/1 [00:00<00:00,  4.43it/s]
SEARCH[Observation]> :  15%|█▍        | 100/677 [00:00<00:01, 304.49it/s]
[82]:
data
0 [2020-03-18T21:53:14.197-04:00]
1 [2020-03-19T21:53:14.197-04:00]
2 [2020-03-19T21:53:14.197-04:00]
3 [2020-03-14T21:53:14.197-04:00]
4 [2020-03-19T21:53:14.197-04:00]
... ...
95 [2020-03-10T21:53:14.197-04:00]
96 [2020-03-15T21:53:14.197-04:00]
97 [2020-03-09T21:53:14.197-04:00]
98 [2020-03-15T21:53:14.197-04:00]
99 [2020-03-09T21:53:14.197-04:00]

100 rows × 1 columns

[83]:
fp.PACK().getPatients(
    ['Patient/1555106'],
    )\
        .getObservations()[:5]\
            .explode()\
                .gatherDates()
GET[Patient]> : 100%|██████████| 1/1 [00:00<00:00,  4.46it/s]
SEARCH[Observation]> :  15%|█▍        | 100/677 [00:00<00:01, 312.13it/s]
[83]:
dates
0 [2020-03-18T21:53:14-04:00, 2020-03-18T21:53:1...
1 [2020-03-19T21:53:14-04:00, 2020-03-19T21:53:1...
2 [2020-03-19T21:53:14-04:00, 2020-03-19T21:53:1...
3 [2020-03-14T21:53:14-04:00, 2020-03-14T21:53:1...
4 [2020-03-19T21:53:14-04:00, 2020-03-19T21:53:1...
... ...
95 [2020-03-10T21:53:14-04:00, 2020-03-10T21:53:1...
96 [2020-03-15T21:53:14-04:00, 2020-03-15T21:53:1...
97 [2020-03-09T21:53:14-04:00, 2020-03-09T21:53:1...
98 [2020-03-15T21:53:14-04:00, 2020-03-15T21:53:1...
99 [2020-03-09T21:53:14-04:00, 2020-03-09T21:53:1...

100 rows × 1 columns

Load#

Exporting Frames#

[84]:
pack.validate(
    ['Patient/1555106']
    )\
        .sendResourcesToFiles(['./data.ignore'])
[84]:
array([ True])

Utils#

Server Profiling#

[ ]:
pack.countServerResources()
resourceType count
0 Account 501
1 ActivityDefinition 284
2 AdverseEvent 116
3 AllergyIntolerance 9031
4 Appointment 29133
... ... ...
141 TestReport 11
142 TestScript 33
143 ValueSet 3041
144 VerificationResult 9
145 VisionPrescription 15

146 rows × 2 columns

Validation#

[8]:
pack.validate(
    ['Patient/1555106']
)

[8]:
data
0 {'resourceType': 'Patient', 'id': '1555106', '...

FHIRPACK Internals#

FHIRPy#

URL Parsing#

[ ]:
import fhirpy.base.utils as fpu
url=fpu.parse_pagination_url('/app/FHIR/r4/Condition?code=C61&_count=1&_sort=-onset-date&identifier=%7C&_Pagination=eyJvZmZzZXQiOjIwfQ%3D%3D')
url
('/app/FHIR/r4/Condition',
 {'code': ['C61'],
  '_count': ['1'],
  '_sort': ['-onset-date'],
  'identifier': ['|'],
  '_Pagination': ['eyJvZmZzZXQiOjIwfQ==']})

Raw fetch_resource()#

[ ]:
pack.client._fetch_resource(
    "Condition/1499041"
)
{'resourceType': 'Condition',
 'id': '1499041',
 'meta': {'versionId': '1',
  'lastUpdated': '2020-10-05T16:29:07.960+00:00',
  'source': '#0sjLd8ZeFHHrRHaX'},
 'clinicalStatus': {'coding': [{'system': 'http://terminology.hl7.org/CodeSystem/condition-clinical',
    'code': 'active'}]},
 'verificationStatus': {'coding': [{'system': 'http://terminology.hl7.org/CodeSystem/condition-ver-status',
    'code': 'confirmed'}]},
 'category': [{'coding': [{'system': 'http://terminology.hl7.org/CodeSystem/condition-category',
     'code': 'encounter-diagnosis',
     'display': 'Encounter Diagnosis'}]}],
 'code': {'coding': [{'system': 'http://snomed.info/sct',
    'code': '22298006',
    'display': 'Myocardial Infarction'}],
  'text': 'Myocardial Infarction'},
 'subject': {'reference': 'Patient/1465609'},
 'onsetDateTime': '2020-04-01T09:52:49-04:00',
 'recordedDate': '2020-04-01T09:52:49-04:00',
 'asserter': {'reference': 'Practitioner/1498740'}}

Raw client.execute()#

[ ]:
pack.client.execute('/baseR4/Condition',
method='get',
params={
    'code':'C61',
    '_sort':'-onset-date',
    '_count': 1,
    '_total':'accurate'
}
).total
1

Customization#

fhirpack.custom.extraction.base#

[ ]:
# the fhirpack.custom package is guaranteed to never be used by us
# use it to customize fhirpack

# pack.unimplementedPluginBaseExtractorMethod()
# pack.unimplementedPluginBaseTransformerMethod()
# pack.unimplementedPluginBaseLoaderMethod()

# pack.unimplementedPluginSampleExtractorMethod()
# pack.unimplementedPluginSampleTransformerMethod()
# pack.unimplementedPluginSampleLoaderMethod()