Giter Club home page Giter Club logo

fhir.resources's Introduction

FHIR® Resources (R5, R4B, STU3, DSTU2)

Supported Python Versions Windows Build Downloads HL7® FHIR®

FHIR (Fast Healthcare Interoperability Resources) is a specification for exchanging healthcare information electronically. It is designed to facilitate the exchange of data between different healthcare systems and applications, and is commonly used to build APIs (Application Programming Interfaces) for healthcare data. It is based on modern web technologies, and is designed to be easy to use and implement. It uses a modular approach, with a set of "resources" that represent different types of healthcare data (such as patients, observations, and diagnoses). These resources can be combined and extended as needed to support a wide range of healthcare use cases.

This "fhir.resources" package is powered by pydantic so faster in performance and optionally orjson support has been included as a performance booster! Obviously it is written in modern python and has data validation built-in. It provides tools and classes for all of the FHIR Resources defined in the FHIR specification, and allows you to create and manipulate FHIR resources in Python. You can then use these resources to build FHIR-based APIs or to work with FHIR data in other ways.

  • Easy to construct, easy to extended validation, easy to export.
  • By inheriting behaviour from pydantic, compatible with ORM.
  • Full support of FHIR® Extensibility for Primitive Data Types are available.
  • Previous release of FHIR® Resources are available.
  • Free software: BSD license

Experimental XML and YAML serialization and deserialization supports. See [Advanced Usages] section!

FHIR® Version Info

FHIR® (Release R5, version 5.0.0) is available as default. Also previous versions are available as Python sub-package (each release name string becomes sub-package name, i.e R4B ). From fhir.resources version 7.0.0; there is no FHIR R4 instead of R4B is available as sub-package.

Available Previous Versions:

  • R4B (4.3.0)
  • STU3 (3.0.2)
  • DSTU2 (1.0.2) [see issue#13][don't have full tests coverage]

Installation

Just a simple pip install fhir.resources or easy_install fhir.resources is enough. But if you want development version, just clone from https://github.com/nazrulworld/fhir.resources and pip install -e .[dev].

Usages

Example: 1: This example creates a new Organization resource with some of its attributes (id, active, name, address):

>>> from fhir.resources.organization import Organization
>>> from fhir.resources.address import Address
>>> data = {
...     "id": "f001",
...     "active": True,
...     "name": "Acme Corporation",
...     "address": [{"country": "Switzerland"}]
... }
>>> org = Organization(**data)
>>> org.resource_type == "Organization"
True
>>> isinstance(org.address[0], Address)
True
>>> org.address[0].country == "Switzerland"
True
>>> org.dict()['active'] is True
True

Example: 2: This example creates a new Organization resource from json string:

>>> from fhir.resources.organization import Organization
>>> from fhir.resources.address import Address
>>> json_str = '''{"resourceType": "Organization",
...     "id": "f001",
...     "active": True,
...     "name": "Acme Corporation",
...     "address": [{"country": "Switzerland"}]
... }'''
>>> org = Organization.parse_raw(json_str)
>>> isinstance(org.address[0], Address)
True
>>> org.address[0].country == "Switzerland"
True
>>> org.dict()['active'] is True
True

Example: 3: This example creates a new Patient resource from json object(py dict):

>>> from fhir.resources.patient import Patient
>>> from fhir.resources.humanname import HumanName
>>> from datetime import date
>>> json_obj = {"resourceType": "Patient",
...     "id": "p001",
...     "active": True,
...     "name": [
...         {"text": "Adam Smith"}
...      ],
...     "birthDate": "1985-06-12"
... }
>>> pat = Patient.parse_obj(json_obj)
>>> isinstance(pat.name[0], HumanName)
True
>>> pat.birthDate == date(year=1985, month=6, day=12)
True
>>> pat.active is True
True

Example: 4: This example creates a new Patient resource from json file:

>>> from fhir.resources.patient import Patient
>>> import os
>>> import pathlib
>>> filename = pathlib.Path("foo/bar.json")
>>> pat = Patient.parse_file(filename)
>>> pat.resource_type == "Patient"
True

Example: 5: This example creates a new Organization resource in python way:

>>> from fhir.resources.organization import Organization
>>> from fhir.resources.address import Address
>>> json_obj = {"resourceType": "Organization",
...     "id": "f001",
...     "active": True,
...     "name": "Acme Corporation",
...     "address": [{"country": "Switzerland"}]
... }

>>> org = Organization.construct()
>>> org.id = "f001"
>>> org.active = True
>>> org.name = "Acme Corporation"
>>> org.address = list()
>>> address = Address.construct()
>>> address.country = "Switzerland"
>>> org.address.append(address)
>>> org.dict() == json_obj
True

Note

Please note that due to the way the validation works, you will run into issues if you are using construct() to create resources that have more than one mandatory field. See this comment in issue#56 for details.

Example: 4: This example creates a new Organization resource using Resource Factory Function:

>>> from fhir.resources import construct_fhir_element
>>> json_dict = {"resourceType": "Organization",
...     "id": "mmanu",
...     "active": True,
...     "name": "Acme Corporation",
...     "address": [{"country": "Switzerland"}]
... }
>>> org = construct_fhir_element('Organization', json_dict)
>>> org.address[0].country == "Switzerland"
True
>>> org.dict()['active'] is True
True

Example: 5: Auto validation while providing wrong datatype:

>>> try:
...     org = Organization({"id": "fmk", "address": ["i am wrong type"]})
...     raise AssertionError("Code should not come here")
... except ValueError:
...     pass

Advanced Usages

FHIR Comments (JSON)

It is possible to add comments inside json like xml, but need to follow some convention, what is suggested by Grahame Grieve; is implemented here.

Also it is possible to generate json string output without comments.

Examples:

>>> observation_str = b"""{
...  "resourceType": "Observation",
...  "id": "f001",
...    "fhir_comments": [
...      "   a specimen identifier - e.g. assigned when the specimen was taken by the orderer/placer  use the accession number for the filling lab   ",
...      "  Placer ID  "
...    ],
...  "text": {
...      "fhir_comments": [
...      "   a specimen identifier - e.g. assigned when the specimen was taken by the orderer/placer  use the accession number for the filling lab   ",
...      "  Placer ID  "
...    ],
...    "status": "generated",
...    "div": "<div xmlns=\"http://www.w3.org/1999/xhtml\">.........</div>"
...  },
...  "identifier": [
...    {
...      "use": "official",
...      "system": "http://www.bmc.nl/zorgportal/identifiers/observations",
...      "value": "6323"
...    }
...  ],
...  "status": "final",
...  "_status": {
...      "fhir_comments": [
...            "  EH: Note to balloters  - lots of choices for whole blood I chose this.  "
...          ]
...  },
...  "code": {
...    "coding": [
...      {
...        "system": "http://loinc.org",
...        "code": "15074-8",
...        "display": "Glucose [Moles/volume] in Blood"
...      }
...    ]
...  },
...  "subject": {
...    "reference": "Patient/f001",
...    "display": "P. van de Heuvel"
...  },
...  "effectivePeriod": {
...    "start": "2013-04-02T09:30:10+01:00"
...  },
...  "issued": "2013-04-03T15:30:10+01:00",
...  "performer": [
...    {
...      "reference": "Practitioner/f005",
...      "display": "A. Langeveld"
...    }
...  ],
...  "valueQuantity": {
...    "value": 6.3,
...    "unit": "mmol/l",
...    "system": "http://unitsofmeasure.org",
...    "code": "mmol/L"
...  },
...  "interpretation": [
...    {
...      "coding": [
...        {
...          "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation",
...          "code": "H",
...          "display": "High"
...        }
...      ]
...    }
...  ],
...  "referenceRange": [
...    {
...      "low": {
...        "value": 3.1,
...        "unit": "mmol/l",
...        "system": "http://unitsofmeasure.org",
...        "code": "mmol/L"
...      },
...      "high": {
...        "value": 6.2,
...        "unit": "mmol/l",
...        "system": "http://unitsofmeasure.org",
...        "code": "mmol/L"
...      }
...    }
...  ]
... }"""
>>> from fhir.resources.observation import Observation
>>> obj = Observation.parse_raw(observation_str)
>>> "fhir_comments" in obj.json()
>>> # Test comments filtering
>>> "fhir_comments" not in obj.json(exclude_comments=True)

Special Case: Missing data

In some cases, implementers might find that they do not have appropriate data for an element with minimum cardinality = 1. In this case, the element must be present, but unless the resource or a profile on it has made the actual value of the primitive data type mandatory, it is possible to provide an extension that explains why the primitive value is not present. Example (required intent element is missing but still valid because of extension):

>>> json_str = b"""{
...    "resourceType": "MedicationRequest",
...    "id": "1620518",
...    "meta": {
...        "versionId": "1",
...        "lastUpdated": "2020-10-27T11:04:42.215+00:00",
...        "source": "#z072VeAlQWM94jpc",
...        "tag": [
...            {
...                "system": "http://www.alpha.alp/use-case",
...                "code": "EX20"
...            }
...        ]
...    },
...    "status": "completed",
...    "_intent": {
...        "extension": [
...            {
...                "url": "http://hl7.org/fhir/StructureDefinition/data-absent-reason",
...                "valueCode": "unknown"
...            }
...        ]
...    },
...    "medicationReference": {
...        "reference": "Medication/1620516",
...        "display": "Erythromycin 250 MG Oral Tablet"
...    },
...    "subject": {
...        "reference": "Patient/1620472"
...    },
...    "encounter": {
...        "reference": "Encounter/1620506",
...        "display": "Follow up encounter"
...    },
...    "authoredOn": "2018-06-16",
...    "requester": {
...        "reference": "Practitioner/1620502",
...        "display": "Dr. Harold Hippocrates"
...    },
...    "reasonReference": [
...        {
...            "reference": "Condition/1620514",
...            "display": "Otitis Media"
...        }
...    ],
...    "dosageInstruction": [
...        {
...            "text": "250 mg 4 times per day for 10 days",
...            "timing": {
...                "repeat": {
...                    "boundsDuration": {
...                        "value": 10,
...                        "unit": "day",
...                        "system": "http://unitsofmeasure.org",
...                        "code": "d"
...                    },
...                    "frequency": 4,
...                    "period": 1,
...                    "periodUnit": "d"
...                }
...            },
...            "doseAndRate": [
...                {
...                    "doseQuantity": {
...                        "value": 250,
...                        "unit": "mg",
...                        "system": "http://unitsofmeasure.org",
...                        "code": "mg"
...                    }
...                }
...            ]
...        }
...    ],
...    "priorPrescription": {
...        "reference": "MedicationRequest/1620517",
...        "display": "Amoxicillin prescription"
...    }
... }"""
>>> from fhir.resources.medicationrequest import MedicationRequest
>>> obj = MedicationRequest.parse_raw(json_str)
>>> "intent" not in obj.dict()

Custom Validators

fhir.resources is providing the extensive API to create and attach custom validator into any model. See more about root validator Some convention you have to follow though, while creating a root validator.

  1. Number of arguments are fixed, as well as names are also. i.e (cls, values).
  2. Should return values, unless any exception need to be raised.
  3. Validator should be attached only one time for individual Model. Update [from now, it's not possible to attach multiple time same name validator on same class]

Example 1: Validator for Patient:

from typing import Dict
from fhir.resources.patient import Patient

import datetime

def validate_birthdate(cls, values: Dict):
    if not values:
        return values
    if "birthDate" not in values:
        raise ValueError("Patient's ``birthDate`` is required.")

    minimum_date = datetime.date(2002, 1, 1)
    if values["birthDate"] > minimum_date:
        raise ValueError("Minimum 18 years patient is allowed to use this system.")
    return values
# we want this validator to execute after data evaluating by individual field validators.
Patient.add_root_validator(validate_gender, pre=False)

Example 2: Validator for Patient from Validator Class:

from typing import Dict
from fhir.resources.patient import Patient

import datetime

class MyValidator:
    @classmethod
    def validate_birthdate(cls, values: Dict):
        if not values:
            return values
        if "birthDate" not in values:
            raise ValueError("Patient's ``birthDate`` is required.")

        minimum_date = datetime.date(2002, 1, 1)
        if values["birthDate"] > minimum_date:
            raise ValueError("Minimum 18 years patient is allowed to use this system.")
        return values
# we want this validator to execute after data evaluating by individual field validators.
Patient.add_root_validator(MyValidator.validate_gender, pre=False)

important notes It is possible add root validator into any base class like DomainResource. In this case you have to make sure root validator is attached before any import of derived class, other than validator will not trigger for successor class (if imported before) by nature.

ENUM Validator

fhir.resources is providing API for enum constraint for each field (where applicable), but it-self doesn't enforce enum based validation! see discussion here. If you want to enforce enum constraint, you have to create a validator for that.

Example: Gender Enum:

from typing import Dict
from fhir.resources.patient import Patient

def validate_gender(cls, values: Dict):
    if not values:
        return values
    enums = cls.__fields__["gender"].field_info.extra["enum_values"]
    if "gender" in values and values["gender"] not in enums:
        raise ValueError("write your message")
    return values

Patient.add_root_validator(validate_gender, pre=True)

Reference Validator

fhir.resources is also providing enum like list of permitted resource types through field property enum_reference_types. You can get that list by following above (Enum) approaches resource_types = cls.__fields__["managingOrganization"].field_info.extra["enum_reference_types"]

Usages of orjson

orjson is one of the fastest Python library for JSON and is more correct than the standard json library (according to their docs). Good news is that fhir.resource has an extensive support for orjson and it's too easy to enable it automatically. What you need to do, just make orjson as your project dependency!

pydantic Field Type Support

All available fhir resources (types) can be use as pydantic's Field's value types. See issue#46 Support for FastAPI pydantic response models. The module fhirtypes.py contains all fhir resources related types and should trigger validator automatically.

Resource.id aka fhirtypes.Id constraint extensibility

There are a lots of discussion here here i.) https://bit.ly/360HksL ii.) https://bit.ly/3o1fZgl about the length of Resource.Id's value. Based on those discussions, we recommend that keep your Resource.Id size within 64 letters (for the seek of intercompatibility with third party system), but we are also providing freedom about the length of Id, in respect with others opinion that 64 chr length is not sufficient. fhirtypes.Id.configure_constraints() is offering to customize as your own requirement.

Examples::
>>> from fhir.resources.fhirtypes import Id
>>> Id.configure_constraints(min_length=16, max_length=128)

Note: when you will change that behaviour, that would impact into your whole project.

XML Supports

Along side with JSON string export, it is possible to export as XML string! Before using this feature, make sure associated dependent library is installed. Use fhir.resources[xml] or fhir.resources[all] as your project requirements.

XML schema validator! It is possible to provide custom xmlparser, during load from file or string, meaning that you can validate data against FHIR xml schema(and/or your custom schema).

Example-1 Export::
>>> from fhir.resources.patient import Patient
>>> data = {"active": True, "gender": "male", "birthDate": "2000-09-18", "name": [{"text": "Primal Kons"}]}
>>> patient_obj = Patient(**data)
>>> xml_str = patient_obj.xml(pretty_print=True)
>>> print(xml_str)
<?xml version='1.0' encoding='utf-8'?>
<Patient xmlns="http://hl7.org/fhir">
  <active value="true"/>
  <name>
    <text value="Primal Kons"/>
  </name>
  <gender value="male"/>
  <birthDate value="2000-09-18"/>
</Patient>
Example-2 Import from string::
>>> from fhir.resources.patient import Patient
>>> data = {"active": True, "gender": "male", "birthDate": "2000-09-18", "name": [{"text": "Primal Kons"}]}
>>> patient_obj = Patient(**data)
>>> xml_str = patient_obj.xml(pretty_print=True)
>>> print(xml_str)
>>> data = b"""<?xml version='1.0' encoding='utf-8'?>
... <Patient xmlns="http://hl7.org/fhir">
...   <active value="true"/>
...   <name>
...     <text value="Primal Kons"/>
...   </name>
...   <gender value="male"/>
...   <birthDate value="2000-09-18"/>
... </Patient>"""
>>> patient = Patient.parse_raw(data, content_type="text/xml")
>>> print(patient.json(indent=2))
{
  "resourceType": "Patient",
  "active": true,
  "name": [
    {
      "text": "Primal Kons",
      "family": "Kons",
      "given": [
        "Primal"
      ]
    }
  ],
  "gender": "male",
  "birthDate": "2000-09-18"
}
>>> with xml parser
>>> import lxml
>>> schema = lxml.etree.XMLSchema(file=str(FHIR_XSD_DIR / "patient.xsd"))
>>> xmlparser = lxml.etree.XMLParser(schema=schema)
>>> patient2 = Patient.parse_raw(data, content_type="text/xml", xmlparser=xmlparser)
>>> patient2 == patient
True
Example-3 Import from file::
>>> patient3 = Patient.parse_file("Patient.xml")
>>> patient3 == patient and patient3 == patient2
True

XML FAQ

  • Although generated XML is validated against FHIR/patient.xsd and FHIR/observation.xsd in tests, but we suggest you check output of your production data.
  • Comment feature is included, but we recommend you check in your complex usages.

YAML Supports

Although there is no official support for YAML documented in FHIR specification, but as an experimental feature, we add this support. Now it is possible export/import YAML strings. Before using this feature, make sure associated dependent library is installed. Use fhir.resources[yaml] or fhir.resources[all] as your project requirements.

Example-1 Export::
>>> from fhir.resources.patient import Patient
>>> data = {"active": True, "gender": "male", "birthDate": "2000-09-18", "name": [{"text": "Primal Kons", "family": "Kons", "given": ["Primal"]}]}
>>> patient_obj = Patient(**data)
>>> yml_str = patient_obj.yaml(indent=True)
>>> print(yml_str)
resourceType: Patient
active: true
name:
- text: Primal Kons
  family: Kons
  given:
  - Primal
gender: male
birthDate: 2000-09-18
Example-2 Import from YAML string::
>>> from fhir.resources.patient import Patient
>>> data = b"""
... resourceType: Patient
... active: true
... name:
... - text: Primal Kons
...   family: Kons
...   given:
...   - Primal
...  gender: male
...  birthDate: 2000-09-18
... """
>>> patient_obj = Patient.parse_raw(data, content_type="text/yaml")
>>> json_str = patient_obj.json(indent=True)
>>> print(json_str)
{
  "resourceType": "Patient",
  "active": true,
  "name": [
    {
      "text": "Primal Kons",
      "family": "Kons",
      "given": [
        "Primal"
      ]
    }
  ],
  "gender": "male",
  "birthDate": "2000-09-18"
}
Example-3 Import from YAML file::
>>> from fhir.resources.patient import Patient
>>> patient_obj = Patient.parse_file("Patient.yml")
>>> json_str = patient_obj.json(indent=True)
>>> print(json_str)
{
  "resourceType": "Patient",
  "active": true,
  "name": [
    {
      "text": "Primal Kons",
      "family": "Kons",
      "given": [
        "Primal"
      ]
    }
  ],
  "gender": "male",
  "birthDate": "2000-09-18"
}

YAML FAQ

  • We are using https://pyyaml.org/ PyYAML library, for serialization/deserialization but if we find more faster library, we could use that. you are welcome to provide us your suggestion.
  • YAML based comments is not supported yet, instead json comments syntax is used! Of course this comment feature is in our todo list.

Allow Empty String

Although this is not good practice to allow empty string value against FHIR primitive data type String. But we in real life scenario, is it unavoidable sometimes.

Examples::

Place this code inside your __init__.py module or any place, just to make sure that this fragment of codes is runtime executed.

>>> from fhir.resources.fhirtypes import String
>>> String.configure_empty_str(allow=True)

FHIR release R4B over R4

FHIR release R4B is coming with not that much changes over the release of R4. So we decided not to create separate sub-package for R4 like STU3, instead there just overlaps on existing R4. This also means that in future, when we will work on R5; there will be sub-package for R4B and no R4. We suggest you to try make a plan to be upgraded to R4B. Here you could find related information dealing-strategy-R4-R4B.

You could find full discussion here #116

Migration (from 6.X.X to 7.0.X)

First of all, you have to correct all imports path, if you wish to keep continue using FHIR release R4B or R4, as those resources are moved under sub-package named R4B. Then if you wish to use current R5 release, read carefully the following documents.

  1. See the full changes history -> https://build.fhir.org/history.html
  2. See complete lists of differences between R5 and R4B -> https://hl7.org/fhir/R5/diff.html
  3. If you are planning to migrate direct from the release R4, then it is important to look at the differences between R4B and R4 here -> https://hl7.org/fhir/R4B/diff.html

Migration (from later than 6.X.X)

This migration guide states some underlying changes of API and replacement, those are commonly used from later than 6.X.X version.

fhir.resources.fhirelementfactory.FHIRElementFactory::instantiate

Replacement: fhir.resources.construct_fhir_element

  • First parameter value is same as previous, the Resource name.
  • Second parameter is more flexible than previous! it is possible to provide not only json dict but also json string or json file path.
  • No third parameter, what was in previous version.

fhir.resources.fhirabstractbase.FHIRAbstractBase::__init__

Replacement: fhir.resources.fhirabstractmodel.FHIRAbstractModel::parse_obj<classmethod>

  • First parameter value is same as previous, json dict.
  • No second parameter, what was in previous version.

fhir.resources.fhirabstractbase.FHIRAbstractBase::as_json

Replacement: fhir.resources.fhirabstractmodel.FHIRAbstractModel::dict

  • Output are almost same previous, but there has some difference in case of some date type, for example py date, datetime, Decimal are in object representation.
  • It is possible to use fhir.resources.fhirabstractmodel.FHIRAbstractModel::json as replacement, when json string is required (so not need further, json dumps from dict)

Note:

All resources/classes are derived from fhir.resources.fhirabstractmodel.FHIRAbstractModel what was previously from fhir.resources.fhirabstractbase.FHIRAbstractBase.

Release and Version Policy

Starting from version 5.0.0 we are following our own release policy and we although follow Semantic Versioning scheme like FHIR® version. Unlike previous statement (bellow), releasing now is not dependent on FHIR®.

removed statement

This package is following FHIR® release and versioning policy, for example say, FHIR releases next version 4.0.1, we also release same version here.

Credits

All FHIR® Resources (python classes) are generated using fhir-parser which is forked from https://github.com/smart-on-fhir/fhir-parser.git.

This package skeleton was created with Cookiecutter and the audreyr/cookiecutter-pypackage project template.

© Copyright HL7® logo, FHIR® logo and the flaming fire are registered trademarks owned by Health Level Seven International

fhir.resources's People

Contributors

a-benkhaled avatar chgl avatar iatechicken avatar itaygoren avatar melvio avatar mmabey avatar nazrulworld avatar simonvadee avatar tshimanga avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fhir.resources's Issues

Error creating resource object

  • fhir.resources version: 6.0.0
  • Python version: 3.6.9
  • Operating System: ubuntu

Description

Creation of resource objects fails for some data types when parsing and creating resource objects from json data. This is the case for decimal within e.g. Quantity and for datetime. Calling .dict() on the object does not return a valid python dict.

What I Did

with open('./Medication-example.json') as json_file:
    json_data = json.load(json_file)

med = Medication.parse_obj(json_data)
med = med.dict()
print(med)

{'id': 'demo-medication-3', 'code': {'coding': [{'code': '116602009', 'display': 'Prednisone (substance)', 'system': 'http://snomed.info/sct'}, {'code': 'H02AB07', 'display': 'prednisone', 'system': 'http://www.whocc.no/atc'}], 'text': 'Deltacortene tablet 25 mg'}, 'form': {'coding': [{'code': '10219000', 'display': 'Tablet', 'system': 'urn:oid:0.4.0.127.0.16.1.1.2.1'}]}, 'ingredient': [{'itemCodeableConcept': {'coding': [{'code': '116602009', 'display': 'Prednisone (substance)', 'system': 'http://snomed.info/sct'}]}, 'strength': {'denominator': {'code': '1', 'system': 'http://unitsofmeasure.org', 'unit': 'tablet', 'value': Decimal('1')}, 'numerator': {'code': 'mg', 'system': 'http://unitsofmeasure.org', 'value': Decimal('25')}}}], 'resourceType': 'Medication'}

med == dict
False

print(json.dumps(med, indent=4))
TypeError: Object of type 'Decimal' is not JSON serializable

---

with open('./Observation-example.json') as json_file:
    json_data = json.load(json_file)

obs = Observation(**json_data)
obs = obs.dict()
print(obs)

{'code': {'coding': [{'code': '718-7', 'display': 'Hemoglobin [Mass/volume] in Blood', 'system': 'http://loinc.org'}], 'text': 'Hemoglobin'}, 'effectiveDateTime': datetime.datetime(2021, 1, 18, 11, 5, 52, tzinfo=datetime.timezone(datetime.timedelta(0, 3600))), 'status': 'final', 'subject': {'reference': 'Patient/156ff0c4-5122-41ca-8bf5-32faf8d3f3a1'}, 'valueQuantity': {'code': 'g/dL', 'system': 'http://unitsofmeasure.org', 'unit': 'g/dL', 'value': Decimal('9.3')}, 'resourceType': 'Observation'}

print(json.dumps(obs, indent=4))
TypeError: Object of type 'datetime' is not JSON serializable

pydantic.errors.ConfigError: duplicate validator function

  • fhir.resources version: 6.0.0b9
  • Python version: 3.7
  • Operating System: AWS Lambda/Jenkins/Docker/localstack/Linux

Description

Tried to redeploy an AWS Lambda function to localstack from a Jenkins container. Deploy works the first time, but subsequent deploys throw the error below after updating the Python lambda function code.

What I Did

The error seems to be thrown when importing CapabilityStatement resource from custom Validator module.

From util.validator.Validator:
from fhir.resources.capabilitystatement import (
    CapabilityStatement, CapabilityStatementRestResource)

Result:
File "/tmp/localstack/lambda_script_l_79309ee6.py", line 29, in <module>
from util.validator import Validator
File "/tmp/localstack/zipfile.e257216d/util/validator.py", line 2, in <module>
from fhir.resources.capabilitystatement import (
File "/tmp/localstack/zipfile.9e07ed84/fhir/resources/capabilitystatement.py", line 18, in <module>
class CapabilityStatement(domainresource.DomainResource):
File "/tmp/localstack/zipfile.9e07ed84/fhir/resources/capabilitystatement.py", line 453, in CapabilityStatement
def validate_required_primitive_elements(
File "/tmp/localstack/zipfile.9e07ed84/pydantic/class_validators.py", line 126, in dec
f_cls = _prepare_validator(f, allow_reuse)
File "/tmp/localstack/zipfile.9e07ed84/pydantic/class_validators.py", line 144, in _prepare_validator
raise ConfigError(f'duplicate validator function "{ref}"; if this is intended, set allow_reuse=True')
pydantic.errors.ConfigError: duplicate validator function "fhir.resources.capabilitystatement.CapabilityStatement.validate_required_primitive_elements"; if this is intended, set allow_reuse=True

Make fhir primitive element field optional if extension value is provided.

https://www.hl7.org/fhir/extensibility.html#Special-Case

Certainly SMART doesn't impose any limits on how FHIR JSON works -- it sounds like there's some question about how FHIR treats extensions that are present in place of required elements. https://www.hl7.org/fhir/extensibility.html#Special-Case provides the official guidance to corroborate @Md Nazrul Islam's note above that "Maybe... when _intent value is provided then the intent field should be optional." -- indeed, required elements can be missing if extensions are provided.

https://chat.fhir.org/#narrow/stream/179218-python/topic/JSON.20representation.20of.20primitive.20elements.20%28validation%29/near/214834853

@Md Nazrul Islam I tested with beta 6.x.x version and this doesn't pass validation:

"_intent": {
"extension": [
{
"url": "http://hl7.org/fhir/StructureDefinition/data-absent-reason",
"valueCode": "unknown"
}
]
},

Am I doing something wrong?

pydantic.error_wrappers.ValidationError: 1 validation error for MedicationRequest
intent
field required (type=value_error.missing)


https://chat.fhir.org/#narrow/stream/179218-python/topic/JSON.20representation.20of.20primitive.20elements.20%28validation%29/near/214840441

@_Stephen Whitney|269724 said:

I see thanks @**Md Nazrul Islam**. It passes validation using open hapi fhir server as written at http://hapi.fhir.org/baseR4/Bundle/$validate

Ok, that is something a little bit long discussion need to be done. Maybe there should be when _intent value is provided then the intent field should be optional.

Parse to XML

  • fhir.resources version: DSTU2
  • Python version: Python 3.7
  • Operating System: Linux

Description

I'm implementing FHIR, version DSTU2 works perfectly but I'd like to know if there is any way to parse the resources (MessageHeader, Patient, ...) to XML format?

What I Did

According to the documentation "binary.as_json()" is the way to convert to Json format but I'm not sure if there is a function to convert to XML

add YAML support

  • fhir.resources version: latest
  • Python version: 3.6+
  • Operating System: MAC/Windows/Linux

Description

would like to add native YAML support so that a Resource object can be created from YAML object or file and serialize as a YAML string.

e.g:

>>> from fhir.resources.organization import Organization
>>> from fhir.resources.address import Address
>>> yaml_str = '''
resourceType: Organization
id: f001
active: true
name: Acme Corporation
address:
  - country: Swizterland
'''
>>> org = Organization.parse_raw(yaml_str)
>>> isinstance(org.address[0], Address)
>>> True
>>> org.address[0].country == "Swizterland"
True
>>> org.dict()['active'] is True
True

and

>>> from fhir.resources.patient import Patient
>>> import os
>>> import pathlib
>>> filename = pathlib.Path("foo/bar.yaml")
>>> pat = Patient.parse_file(filename)
>>> pat.resource_type == "Patient"
True

and

>>> pat.as_yaml()
'''
resourceType: Organization
id: f001
active: true
name: Acme Corporation
address:
  - country: Swizterland
'''

See pyyaml.org

Question regarding typing and mypy

  • fhir.resources version: 6.1.0
  • Python version: 3.7.5
  • Operating System: Ubuntu 18.04

Description

Hey there! Thanks for all the great work on this package! Looking to start using this pretty heavily internally, so many niceties come out of the box with pydantic-based libraries like this.

One slight hiccup I'm seeing in the dev experience is the mismatch between the type annotation and the actual underlying type at runtime.

For example, the fhir.resources.patient.Patient model has a field type of address: typing.List[fhirtypes.AddressType] where AddressType looked like a wrapper around plain dict. Initially I thought this was a little weird, but maybe makes sense if we wanted to postpone validation until that specific field was used and unpacked into its corresponding pydantic type.

When I went to implement our address functionality, I used logic to the effect of address_pydantic = Address(**patient.address[0]) which threw the following error:

TypeError: ModelMetaclass object argument after ** must be a mapping, not Address

So at runtime, the real type of patient.address is List[fhir.resources.address.Address] rather than the dict based type provided in the annotations.

This unfortunately poses an issue for typecheckers such as mypy. The typechecker expects the value to be an AbstractType during runtime, so it won't allow the usage of pydantic fields (for example patient.address[0].state) unless we put cast( calls all over the place, opening ourselves up to type-related bugs that we were trying to avoid by using a pydantic-based lib.

Question

Would you mind expanding on this design decision to have the AbstractType annotations on each of the pydantic type's fields, instead of the actual pydantic type that is seen at runtime? Does anyone else use typecheckers in combination with this library, without needing to cast everywhere? (Certainly open to using a different type checking system as well if this is just a limitation of mypy)

There very well could be information out there on this topic already, however I was not able to find it in the docs. Let me know if I missed it or if my library usage above does not align with what is expected!

Help on converting XML to FHIR format

  • fhir.resources version:
  • Python version:
  • Operating System:

Description

Hi I am a newbie to FHIR data formats. This might be a dumb questions as well, is there any easy way to convert bunch of XML's to FHIR format? any suggestions on blog/tutorials for reference?

Any help is appreciated!!
Describe what you were trying to get done.
Tell us what happened, what went wrong, and what you expected to happen.

What I Did

Paste the command(s) you ran and the output.
If there was a crash, please include the traceback here.

(version 6) How to build resources with extensions from dict?

  • fhir.resources version: 6.0.0
  • Python version: 3.6.8
  • Operating System: CentOS 7

Description

I'm trying to construct a basic resource with extension fields. In the example below I'm trying to add an extension to Patient resource.

What I Did

        result = fhir.resources.patient.Patient(**{
            'id': str(self.id),
            '_banana': {
                'extension': [{
                    'url': 'https://tempuri.org',
                    'valueInteger': self.specialid,
                }],
            },
...
)
_banana
 extra fields not permitted (type=value_error.extra).

Default values for list fields

  • fhir.resources version: 6.0.0
  • Python version: 3.8
  • Operating System: MacOS

Description

I noticed that all the optional objects which has amount of 0...1 are defaulted to None which is logical.
Why do we set the default value for fields with amount 0...* to be None as well and not []?

resourceType last key instead of first in json

  • fhir.resources version: 6.0.0
  • Python version: 3.7
  • Operating System: windows 10

Description

I've built a diagnostic report resource but when I print json the resourceType is the last key. I was expecting the resourceType to be the first key. How can I fix this? please

What I Did

from fhir.resources.diagnosticreport import DiagnosticReport
from fhir.resources.identifier import Identifier

diagnosticreport = DiagnosticReport.construct()
diagnosticreport.status = 'final'
identifier = Identifier()
diagnosticreport.identifier = list()
identifier.value = '21-48608871'
diagnosticreport.identifier.append(identifier)
diagnosticreport.issued = '2021-03-05T11:45:33+11:00'

print(diagnosticreport.json())

{"identifier": [{"value": "21-48608871"}], "issued": "2021-03-05T11:45:33+11:00", "status": "final", "resourceType": "DiagnosticReport"}

STU3 support missing in pypy version.

  • fhir.resources version: STU3
  • Python version: 2.7
  • Operating System: *nix

Description

STU3 code not included in pypy version.

What I Did

I used 'pip install' to install this package expecting to be able to use the code to support STU3 and R4 projects. However I discovered that the STU3 code was missing.

Getting validation errors that don't make sense

  • fhir.resources version: 6.0.0b3 (4bc72ae)
  • Python version: 3.8.3
  • Operating System: Mac OS

Description

I'm attempting to use FHIR (DSTU2) Bundles I already had in my tests against the current version of fhir.resources (all tests pass with version 5.1.1 of fhir.resources) and I'm getting lots of validation errors. This one is the first I've looked into in depth, but I'm hoping that by figuring out this one I'll have a better chance debugging the others on my own.

I have a JSON bundle (attached below, but renamed to .txt so GitHub would let me upload it) I have tried to use to create a resource object. I continue to get the error value is not a valid list (type=type_error.list), but that doesn't make any sense since the value for the coding is a Python list.

What I Did

See iPython screenshot below. The output from [7] is the most informative, yet super confusing since the value is truly a list. I've triple-checked the FHIR spec (DSTU2) and everything seems to conform to it, and nothing that I can see in the pydantic model seems to be causing the problem. And creating a Coding object from data["entry"][0]["resource"]['participant'][0]['type'][0]['coding'][0] causes no problems either.

image

test_encounter_bundle.txt

how to use the 'server' attribute?

Hi all,

This is a really amazing library. I'm a physician, and I'm starting to learn about FHIR and how to use it and this library has been a great starting place.

I'm having a hard time finding documentation on the 'server' attribute that is found in many objects. For example if I create a new patient:

data = {'name':{'given':['Bob'], 'family':'Smith'}}
new_p = Patient(**data)

There is now a new_p.server attribute. I would like to know if I set the server attribute am I able to then just call new_p.create() to create an instance on the server? Or if I want to find patients on a server do I just set the search parameters in a Patient object and then call .search().

For context, I've been learning how to post and search for FHIR objects on a locally running HAPI FHIR server with this docker image.

Any help is much appreciated.

EDIT
I now realize that I was using an old version of the library. I would like to know if similar functionality is available in the current version that from the master branch on this repository ('6.0.0b11').

How to use this as a FHIR Server

  • fhir.resources version: 5.1.0
  • Python version: 3.7.6
  • Operating System: Linux

Description

Disclaimer I am getting started with FHIR.
My high level goal is to implement some basic FHIR resources on a flask API server.
Expose some FHIR search capabilities. Run some basic queries on a backend store, populate FHIR resource objects, return these resources in FHIR-compliant JSON structure response.

What I Did

>>> from fhir.resources.fhirelementfactory import FHIRElementFactory
>>> json_dict = {"resourceType": "Organization", "id": "foo", "active": True, "name": "acme", "address": [{"country": "us"}]}
>>> org = FHIRElementFactory.instantiate('Organization', json_dict)
>>> org.address[0].country == "Swizterland"
False
>>> org.address[0].country == "us"
True
>>> org.as_json()['active'] is True
True
>>> repr(org)
'<fhir.resources.organization.Organization object at 0x02DF5250>'
>>> org.as_json()
{'id': 'foo', 'active': True, 'address': [{'country': 'us'}], 'name': 'acme', 'resourceType': 'Organization'}

All of this is fine but that JSON is not a true FHIR resource response as I understand it to be. For example it is missing things like resourcetype

Is my understanding of the above correct? Is there a better way? Am I getting too old for programming and need to check myself into a retirement community?

DateTime is not FHIR spec compliant

  • fhir.resources version: 6.1.0
  • Python version: 3.8.5
  • Operating System: Ubuntu Focal

Description

The fhir.resources DateTime object is not spec compliant. The spec (and the doc comments) both require a +zz:zz or Z timezone suffix, it is using datetime.isoformat() which does not do this. Python isoformat() violates ISO8601 https://stackoverflow.com/a/23705687/1480205

See DateTime.to_string
https://github.com/nazrulworld/fhir.resources/blob/main/fhir/resources/fhirtypes.py#L511

We are trying to interoperate with a partner and they are unable to consume the FHIR JSON we generate since the DateTimes are all wrong. We think a workaround will be to correctly pre-format the datetime and use the str instead of the actual datetime object, it looks like to_string will leave it alone in that case.

What I Did

All DateTimes in FHIR objects are of the form 2021-05-07T21:41:57.120530
This is not compliant with the doc comment or the spec https://www.hl7.org/fhir/datatypes.html#dateTime

>>> from fhir.resources.annotation import Annotation
>>> import datetime
>>> a = Annotation(text="abc", time=datetime.datetime.utcnow())
>>> a.dict()
{'text': 'abc', 'time': datetime.datetime(2021, 5, 7, 22, 33, 55, 557605)}
>>> a.json()
'{"text": "abc", "time": "2021-05-07T22:33:55.557605"}'

treating empty lists ('[]') as None

  • fhir.resources version: R4
  • Python version: 3.7.3
  • Operating System: Mac OS

Description

I am creating a FHIR object using the models and a spreadsheet as a source
When there is repeating element and their is no data in the source, my code creates an empty list.
This empty list is preserved unlike when the element is None. I think when an element is evaluated to these Falsy types listed below I think it should behave the same ... namely the element value should be None. Note that in FHIR "when present, elements cannot be empty - they SHALL have a value attribute, child elements, or extensions".

  1. None
  2. [] - an empty list
  3. {} - an empty dict
  4. () - an empty tuple
  5. '' - an empty str
  6. b'' - an empty bytes
  7. set() - an empty set.

What I Did

cs = construct_fhir_element('CapabilityStatement',dict(
id = meta.id,
url = f'{canon}CapabilityStatement/{meta.id}',
version = meta.version,
# more elements
instantiates = [i for i in meta.instantiates.split(',') if i],  #<<<< this evaluates to '[]'  instead of None 
))

Note that I can code around this but I think this should be the default behavior of the classes as a convienence.

Test file

  • fhir.resources version:
  • Python version:
  • Operating System:

Description

I would like to run the test suite for DSTU (or at least review the test example files), but the json files that the tests use don't seem to be included in this repo.

Mypy errors accessing resources fields

Im using python 3.8 and trying to upgrade from version 6.0.0b5 to 6.1.0.
The code is

from fhir.resources.codeableconcept import CodeableConcept
from fhir.resources.condition import Condition

Condition(subject={}, code=CodeableConcept()).code.coding

When using version 6.0.0b5 and running mypy --ignore-missing-imports a.py I get:
Success: no issues found in 1 source file

Using version 6.1.0 and running the same function I get:

a.py:4: error: "CodeableConceptType" has no attribute "coding"
Found 1 error in 1 file (checked 1 source file)

Looking at the classes, we can see that the annotations are using FooType, for example CodeableConceptType and not CodeableConcept which makes mypy not being able to parse the library.
If I understand correctly, the rational to use FooType classes is otherwise we would have circle imports.

How can we make mypy parse this library?
Or, what changed between 6.0.0b5 to 6.1.0 that can affect it? Were type hints added to the library? If so, I think its wrong to do so :)

datatype String lacks extension support

  • fhir.resources version: master checkout as of June 22nd, 2020

Description

FHIR allows for (for example) Strings to have extensions.
An example of that can be found here: https://www.hl7.org/fhir/extensibility-examples.html#2.5.2.2.3

In the json representation this looks like this:

{
  "name": [
    {
      "given": [
        "Adam",
        "A",
        "Adam"
      ],
      "_given": [
        {
          "extension": [
            {
              "url": "http://hl7.org/fhir/StructureDefinition/iso21090-EN-qualifier",
              "valueCode": "BR"
            }
          ]
        },
        {
          "extension": [
            {
              "url": "http://hl7.org/fhir/StructureDefinition/iso21090-EN-qualifier",
              "valueCode": "IN"
            }
          ]
        },
        {
          "extension": [
            {
              "url": "http://hl7.org/fhir/StructureDefinition/iso21090-EN-qualifier",
              "valueCode": "CL"
            }
          ]
        }
      ]
    }
  ]
}

What I get though, is a simple python string, so I've lost the extensions.

patient = fhir.resources.STU3.patient.Patient(**json.loads("[json here]"))
patient.name[0].given
['Adam', 'A', 'Adam']

Here's a full example:

import json
import fhir.resources.STU3.patient

patient_json = """
{
  "resourceType": "Patient",
  "name": [
    {
      "given": [
        "Adam",
        "A",
        "Adam"
      ],
      "_given": [
        {
          "extension": [
            {
              "url": "http://hl7.org/fhir/StructureDefinition/iso21090-EN-qualifier",
              "valueCode": "BR"
            }
          ]
        },
        {
          "extension": [
            {
              "url": "http://hl7.org/fhir/StructureDefinition/iso21090-EN-qualifier",
              "valueCode": "IN"
            }
          ]
        },
        {
          "extension": [
            {
              "url": "http://hl7.org/fhir/StructureDefinition/iso21090-EN-qualifier",
              "valueCode": "CL"
            }
          ]
        }
      ]
    }
  ]
}
"""

patient = fhir.resources.STU3.patient.Patient(**json.loads(patient_json))
print(patient.name[0].given)
# (Pdb) patient.name[0].given
# ['Adam', 'A', 'Adam']

Example 5 and Encounter validation is failing

Greetings, I am investigating this library to use in a ETL step for a data integration and looking to create FHIR data structures. I've never used pydantic before so please forgive my ignorance :) Could I get some help with why this is failing?

  • fhir.resources version: commit: f35c98f (most recent)
  • Python version: 3.7.9
  • Operating System: Windows 10

Description

I am trying to create an encounter:

from fhir.resources.encounter import Encounter
e: Encounter = Encounter(**{"class": 'inpatient', "status": "planned"})

What I Did

Results in a confusing error. Especially since it is talking about "jsondecode" error.

Traceback (most recent call last):
  File "test.py", line 62, in <module>
    fetchsome(cur, handleRow, some=100)
  File "test.py", line 39, in fetchsome
    rowFunc(row)
  File "test.py", line 52, in handleRow
    e: Encounter = Encounter(**{"class": 'inpatient', "status": "planned"})
  File "...\lib\site-packages\fhir.resources-6.0.0b7.dev0-py3.7.egg\fhir\resources\fhirabstractmodel.py", line 126, in __init__
    BaseModel.__init__(__pydantic_self__, **data)
  File "pydantic\main.py", line 362, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 1 validation error for Encounter
class -> __root__
  expected value at line 1 column 1: line 1 column 1 (char 0) (type=value_error.jsondecode; msg=expected value at line 1 column 1; doc=; pos=0; lineno=1; colno=1)

In addition to above Example 5 doesn't seem to work either:

from fhir.resources.organization import Organization
org = Organization.construct()

results in

Traceback (most recent call last):
  File "test.py", line 17, in <module>
    org = Organization.construct()
  File "...\lib\site-packages\fhir.resources-6.0.0b7.dev0-py3.7.egg\fhir\resources\fhirabstractmodel.py", line 386, in construct
    m, "__dict__", {**deepcopy(cls.__field_defaults__), **values}
AttributeError: type object 'Organization' has no attribute '__field_defaults__'

backboneelement introduced cyclical import as of 8fe3dbcd

  • fhir.resources version: 5.1.1.dev0
  • Python version: 3.6.9
  • Operating System: MacOS

Description

In 8fe3dbc, when Black was used on the code and unified the import statements, the import of extension was moved from the bottom of the file to the top. Any code that uses backboneelement.BackboneElement (in my case an OperationOutcome) now triggers a cyclical import as can be seen in the attached screenshot. Briefly:

  • backboneelement imports extension
  • extension imports annotation
  • annotation imports fhirreference
  • fhirreference imports bundle
  • bundle attempts to import backboneelement, which technically succeeds, but since the first import of backboneelement hasn't completed, the BackboneElement class isn't available and the import in bundle fails.

Expected behavior is for an import of operationoutcome.OperationOutcome to succeed.

What I Did

Text version of the attached image:

     from fhir.resources.DSTU2.operationoutcome import OperationOutcome
   File "/home/app/.local/share/virtualenvs/ehr-TNUJx0lF/lib/python3.6/site-packages/fhir/resources/DSTU2/operationoutcome.py", line 8, in <module>
     from . import backboneelement, codeableconcept, domainresource
   File "/home/app/.local/share/virtualenvs/ehr-TNUJx0lF/lib/python3.6/site-packages/fhir/resources/DSTU2/backboneelement.py", line 8, in <module>
     from . import element, extension
   File "/home/app/.local/share/virtualenvs/ehr-TNUJx0lF/lib/python3.6/site-packages/fhir/resources/DSTU2/extension.py", line 8, in <module>
     from . import (address, annotation, attachment, codeableconcept, coding,
   File "/home/app/.local/share/virtualenvs/ehr-TNUJx0lF/lib/python3.6/site-packages/fhir/resources/DSTU2/annotation.py", line 8, in <module>
     from . import element, fhirdate, fhirreference
   File "/home/app/.local/share/virtualenvs/ehr-TNUJx0lF/lib/python3.6/site-packages/fhir/resources/DSTU2/fhirreference.py", line 116, in <module>
     from . import bundle
   File "/home/app/.local/share/virtualenvs/ehr-TNUJx0lF/lib/python3.6/site-packages/fhir/resources/DSTU2/bundle.py", line 64, in <module>
     class BundleEntry(backboneelement.BackboneElement):
 AttributeError: module 'fhir.resources.DSTU2.backboneelement' has no attribute 'BackboneElement'

image

What's the best way to compare periods?

  • fhir.resources version: 5b98990
  • Python version: 3.8.3 🎉
  • Operating System: Mac OS 10.15.5

I've been taking a look at the pydantic refactored code, and I think it's going to be a great thing for the project. As I've looked at how to adapt my code to the changes, one thing has come up that I can't quite find a good answer for.

In the project I'm working on, there are lots of instances where I need to be able to compare one Period with another. For example, I need to know which period is more recent than another. This gets really tricky because FHIR's dateTime values have such a wide range of acceptable granularity. For example, I might need to decide if a period with a starting year, month, and day is more recent than a period with an ending year and month (but no day).

The solution I came up with previously was to not depend on the Python datetime objects offered by the previous (<=5.0.1) versions of fhir.resources, but instead parse the FHIR start and end dateTime values myself and slightly differently, with start dates filling any missing values for month, day, hour, minute, second, and microsecond with the lowest (earliest) value (e.g., month=1, hour=0, etc). Likewise, I parsed end dateTime values by filling any missing values with the highest (latest) value possible (e.g., month=12, minute=59).

The above solution depended on using the origval property of the fhir.resources.period.Period object, which I used as input to the custom parsers I just described. And this is where the problem arises: The new pydantic models do not retain the original string extracted from the FHIR-formatted JSON.

I've tried to do some research into possible ways to address this, but due to my lack of familiarity with the ins and outs of pydantic, I've probably missed some viable solutions. Anyway, here are some ideas:

  1. Alter the pydantic models for Date, DateTime, Instant, and Time to make the original string value available (the ones defined in fhir/resources/{DSTU2,STU3,}/fhirtypes.py. I list this first because I think it will be the least labor-intensive, assuming there's a way to actually accomplish this with pydantic. From what I understand, these model types would need to use generic classes

    • Pros:
      • Provides enough flexibility so that custom parsers (like my own) can do what is necessary for the specific circumstances.
    • Cons:
      • Breaks some of the purity of the pydantic models/types of the library.
      • Doesn't provide a generic solution, meaning projects must each develop their own approach to solving the same problem of comparing date, dateTime, and time values.
      • May require the classes to start returning instances of themselves from the validate methods instead of the classes from the datetime library. This is only a half-con though since I think the pydantic docs recommend doing it that way anyway.
  2. Create a custom class for Periods that allow for fuzzy data (according to the FHIR spec) while also providing operators that allow for comparison in a way that makes sense (I drew up a few examples of this but eventually decided they were confusing).

    • Pros:
      • Provides a "batteries included" kind of way to compare Period elements without library users having to implement anything themselves.
    • Cons:
      • Implementation could get a little complicated.

That's all I've got so far. I'm hoping that by discussing this we can find something that will be useful to many of the library's users.

confusing error message "name 'self' is not defined"

  • fhir.resources version: 5.0.0
  • Python version: 3.6
  • Operating System: Microsoft Windows 10 Enterprise

Description

Confusing error message "name 'self' is not defined" when using method "update_with_json(self, jsondict)", while providing an incorrect format for jsondict.

What I Did

The cause of this confusing message I belive is a bug in FHIRAbstractBase._with_json_dict(cls, jsondict). The body of this classmethod refers to "self" which is not defined in this namespace.

(note: I have no experience on github, I hope this is the correct way to report small bugs)

Remove "tests" from installed package

  • fhir.resources version: latest
  • Python version: 3.8
  • Operating System: everything

Description

When installing the package, the "tests" folder is being installed which takes 10MB from the system. It might not sound a lot but when using AWS serverless applications, you have 250MB storage and 10MB can be critical.
The problem is in the project "setup.py" by using "find_packages" which finds the tests directory.
It needs to be filtered out.

What I Did

Opened a ticket 😃

Any resource: "id" element fails validation with underscore in value

  • fhir.resources version: 6.1.0
  • Python version: Python 3.8.6
  • Operating System: Linux

Description

If resource.id element has an underscore, validation fails

What I Did

resource = {'resourceType': 'Patient', 'id': 'abc_1234'}
Patient(**resource)

pydantic.error_wrappers.ValidationError: 1 validation error for Patient
id
string does not match regex "^[A-Za-z0-9-.]+$" (type=value_error.str.regex; pattern=^[A-Za-z0-9-.]+$)

How to build a Model in Django using this Package

  • fhir.resources version: 6.1
  • Python version: 3.8
  • Operating System: iOS

Description

There's no issue as such. I'm trying to create a Django model using this project, for example, the patient resource.

Does this package support creation of a model?

ExplanationOfBenefit

  • fhir.resources version: 6.1.0
  • Python version: 3.8.3
  • Operating System: MacOS Catalina

Description

Describe what you were trying to get done.
Tell us what happened, what went wrong, and what you expected to happen.

I have a record of ExplanationOfBenefit in json format.
When I pass it to fhir.resources.ExplanationOfBenefit it get the following error.

ValidationError: 2 validation errors for ExplanationOfBenefit __root__ -> created field required (type=value_error.missing) __root__ -> outcome field required (type=value_error.missing)

What I Did

record = records[5945] from fhir.resources.explanationofbenefit import ExplanationOfBenefit Eob = ExplanationOfBenefit(**record)

How can I know what values are missing and is there a way to print the missing values

contact.telecom.extension.valueUrl does not validate phone number values

  • fhir.resources version: 6.1.0
  • Python version: 3.7
  • Operating System: Ubuntu 18.04

Description

I'm trying to construct a Patient resource after fetching it via a FHIR call.

What I Did

I fetched some data and tried to construct it using the Patient model class.

The contact field for the resulting patient information was

"contact": [
    {
        "relationship": [{ /* a relationship object */}],
        "name": { /* a name object */ },
        "telecom": [{
            "extension": [{
                "valueUrl": "(123)123-1234",
                "url": "http://hl7.org/fhir/StructureDefinition/iso21090-TEL-address"
            }],
            /* rest of the telecom object*/
        }]
    }],
contact -> 0 -> telecom -> 0 -> extension -> 0 -> valueUrl
invalid or missing URL scheme (type=value_error.url.scheme)

The url rfc defines that parenthesis are not valid so the sender seems to be at fault. Is there any way for me to continue to use the library to construct this Patient resource and specifically ignore validating this field?

Add DSTU2 Support

  • fhir.resources version: DSTU2
  • Python version: 2.7
  • Operating System: *nix

Description

I am supporting several FHIR projects and I need support for DSTU2 and well as STU3 and R4.

What I Did

I looked at the code and discovered that code supporting STU3 was included, but not DSTU2.

Not required property becomes mandatory

  • fhir.resources version: 5.1.1
  • Python version: 3.7.5
  • Operating System: linux

Description

Validating claim without focal.

It's mandatory in Python: https://github.com/nazrulworld/fhir.resources/blob/5.1.1/fhir/resources/claim.py#L671

("focal", "focal", bool, "boolean", False, None, True)

(Where the last True is for not_optional or mandatory/required as far as I could see.)

(BTW, the same is true for Golang - https://github.com/samply/golang-fhir-models/blob/c26453f28c9f71b1e4e6870ecd633d38b665f005/fhir-models/fhir/claim.go#L116)

In the json schema this field is not required:

    "Claim_Insurance": {


      "properties": {
   

        "focal": {
          "description": "A flag to indicate that this Coverage is to be used for adjudication of this claim when set to true.",
          "$ref": "#/definitions/boolean"
        },
        "_focal": {
          "description": "Extensions for focal",
          "$ref": "#/definitions/Element"
        },
      

        "coverage": {
          "description": "Reference to the insurance card level information contained in the Coverage resource. The coverage issuing insurer will use these details to locate the patient\u0027s actual coverage within the insurer\u0027s information system.",
          "$ref": "#/definitions/Reference"
        }


      },
      "additionalProperties": false,
      "required": [
        "coverage"
      ]
    },

What I Did

    with open("claim.json") as claim_file:
        claim_json = json.load(claim_file)
        claim = Claim(claim_json)
        return claim

Create remaning DSTU2 FHIR resources.

FHIR release DSTU2 still needed!

I use fhir.resources as part of my work for a company that integrates with EHR systems using FHIR. The two biggest EHR systems I know of (Epic and Cerner) currently only support FHIR v1.0.2 (DSTU 2). My understanding is that there were so many issues with STU3 that hardly systems will ever support it and will instead migrate from DSTU 2 straight to R4. (I promise I'm not trying to sound dramatic here, that's literally what my company's Product team has told me these companies have said.)

Recent changes (specifically commit 4eb0790) remove support for DSTU 2, which effectively makes the latest version of the fhir.resources library unusable for my team.

If I may ask, what was the reasoning behind the removal of support for DSTU 2? Is there something I could contribute to make it worth the effort to continue supporting that version of the FHIR spec?

Validation of enum values

  • fhir.resources version: 6.0.0b4.dev0
  • Python version: 3.8
  • Operating System: macOS

Description

I want FHIRAbstractModel to be able to validate enum values. I think it's already supported by the current version but I couldn't find how to do it.

class Patient(domainresource.DomainResource):
    ...
    gender: fhirtypes.Code = Field(
        None,
        alias="gender",
        title="male | female | other | unknown",
        description=(
            "Administrative Gender - the gender that the patient is considered to "
            "have for administration and record keeping purposes."
        ),
        # if property is element of this resource.
        element_property=True,
        # note: Enum values can be used in validation,
        # but use in your own responsibilities, read official FHIR documentation.
        enum_values=["male", "female", "other", "unknown"],
    )

I don't quite understand what this means:
note: Enum values can be used in validation, but use in your own responsibilities, read official FHIR documentation.
because the pydantic model does not enforce the value of this field.

What I Did

from fhir.resources import construct_fhir_element
resource = {"resourceType": "Patient", "gender": "toto"}
construct_fhir_element(resource["resourceType"], resource)
# everything went fine, but it should raise a validation error

publish new version

Hey,
When would the new version be released with the changes in the change log?
Thanks

`construct_fhir_element` change the given dict

  • fhir.resources version: 6
  • Python version: 3.8
  • Operating System: mac

Description

Running the following code:

from fhir.resources import construct_fhir_element
d = {'resourceType': 'Patient', 'contained':[{'resourceType': 'Patient'}]}
construct_fhir_element('Patient', d)

end up changing the value of d to {'resourceType': 'Patient', 'contained': [{}]}

I would expect it to not change the given object. Moreover, because it doesn't delete resourceType from the outter dictionary, I assume this behavior is not expected.
Is it on purpose?
Thanks,
Itay

Dates are not converted when using `construct_fhir_element`

  • fhir.resources version: 6.0.0b5
  • Python version: 3.8
  • Operating System: mac

Description

When creating a resource with construct_fhir_element and specifying a date as a string, the date is not converted to Date but rather stay as string. Yet, the regex pattern is verified.
I tried multiple resources and it happens for all of them.

What I Did

from fhir.resources import construct_fhir_element
p = construct_fhir_element('Patient', {'birthDate': '2012'})
print(p.birthDate)  # prints `"2012"`
print(type(p.birthDate))  # prints `str`

The same happen when using Patient.parse_obj.

Edit:
The following Patient.parse_obj({'birthDate': '2012-01-01'}).birthDate indeed returns datetime.date.

FHIR Reference does not resolve references in Bundle

  • fhir.resources version: 5.1.1.
  • Python version: 3.8

Description

Resolution of references within a Bundle fails unless an instance to a server is provided (and a couple of additional undocumented assumptions are met).

The problem is in fhir.resources.fhirresource.FHIRResource.resolved(). In case of a relative reference, the method attempts to build the fullUrl based on the value of bundle.server.base_uri. If no server instance is set, resolution fails.

The standard states

If the reference is not an absolute reference, convert it to an absolute URL:

if the reference has the format [type]/[id], and
if the fullUrl for the bundle entry containing the resource is a RESTful one (see the RESTful URL regex)
    extract the [root] from the fullUrl, and append the reference (type/id) to it
    then try to resolve within the bundle as for a RESTful URL reference.
    If no resolution is possible, then the reference has no defined meaning within this specification

The important part is extract the [root] from the fullUrl, and append the reference (type/id) to it. For example:

base_uri = '/'.join(bundle_entry.fullUrl.split('/')[:-2])
fullUrl = f'{base_uri}/{resource_type}/{resource_id}'

The bundle.server.base_uri is not needed (and should not be expected to be present).

What I Did

import requests
from fhir.resources.bundle import Bundle
from fhir.resources.patient import Patient

response = requests.get('https://www.hl7.org/fhir/bundle-example.json')
bundle = Bundle(response.json())

patient = bundle.entry[0].resource.subject.resolved(Patient)

results in

Not implemented: resolving absolute reference to resource Patient/347

Version 5 method `__eq__`

  • fhir.resources version: 5
  • Python version: 3.8
  • Operating System: mac

Description

As far as I can see, there is no method __eq__ implemented. Am I missing something? Is it supposed to be added in the future?
Thanks

Validation error if resource id is set before any mandatory fields

  • fhir.resources version: 6.1.0
  • Python version: 3.9.0 (64-bit)
  • Operating System: Windows 10/Linux

Description

It seems that the validation success depends on when resource.id is set - at least that's my interpretation for now. Is this expected behavior?

What I Did

The following crashes with a validation error:

from fhir.resources.observation import Observation

o = Observation.construct()
o.id = "o123"
o.status = "final"

print(o.json())
pydantic.error_wrappers.ValidationError: 1 validation error for Observation
__root__ -> status
  none is not an allowed value (type=type_error.none.not_allowed)

but switching o.id and o.status around works fine:

from fhir.resources.observation import Observation

o = Observation.construct()
o.status = "final"
o.id = "o123"

print(o.json())
{"id": "o123", "status": "final", "resourceType": "Observation"}

code validation questions

  • fhir.resources version: current
  • Python version: 3.7
  • Operating System: Mac OS

Description

when instantiate a FHIR Resource with a required valueset - Observation.status, I expected it to be validated based upon the fixed set of codes ( http://hl7.org/fhir/valueset-observation-status.html ). What is the status of code validataion? I think that it would possble to at least validate the required bindings where the codes are enumerated.

What I Did

from fhir.resources import construct_fhir_element
obs = construct_fhir_element('Observation', {"status":"foo", "code": {}})

I expected the value "foo" to cause a pydantic validation error.

When are Falsy values evaluated as None?

  • fhir.resources version: R4
  • Python version: 3.7.3
  • Operating System: Mac OS

Description

I am creating a FHIR object using the models and a spreadsheet as a source
When there is repeating element and their is no data in the source, my code creates an empty list.
This empty list is preserved unlike when the element is None. I think when an element is evaluated to these Falsy types listed below I think it should behave the same ... namely the element value should be None. Note that in FHIR "when present, elements cannot be empty - they SHALL have a value attribute, child elements, or extensions".

  1. None
  2. [] - an empty list
  3. {} - an empty dict
  4. () - an empty tuple
  5. '' - an empty str
  6. b'' - an empty bytes
  7. set() - an empty set.

What I Did

cs = construct_fhir_element('CapabilityStatement',dict(
id = meta.id,
url = f'{canon}CapabilityStatement/{meta.id}',
version = meta.version,
# more elements
instantiates = [i for i in meta.instantiates.split(',') if i], 
imports = [i for i in meta.imports.split(',') if i],   #<<<< this evaluates to '[]'  instead of None 
))


print(cs.json(indent=2))

...
  "kind": "requirements",
  "instantiates": [
    "foo"
  ],
  "imports": [],  <<<<<<<<<<<<<  the empty list as value!
  "fhirVersion": "4.0.1",
....

As a convenience, I would like to see these falsy values be treated like None instead of persisting.

How to discover the element type?

  • fhir.resources version: current
  • Python version: 3.7
  • Operating System: Mac

Description

Describe what you were trying to get done.

I am trying to discover the simplest and most direct way to discover the type for an element:

ideally this :

      1 from fhir.resources.observation import Observation
      2 
----> 3 Observation.resource_type

AttributeError: type object 'Observation' has no attribute 'resource_type'

obviously this isn't going to work on this instance but don't know the best way to do this on the
class

What I Did

obs_obj = Observation.parse_file("Observation_wt.yml")
for i in obs_obj.element_properties():
    try:
        print(f'i.name={i.name}, itype = {i.type_.__resource_type__}')
    except:
        print(f'i.name={i.name}')

i.name=id
i.name=implicitRules
i.name=language
i.name=meta, itype = Meta
i.name=contained, itype = Resource
i.name=extension, itype = Extension
i.name=modifierExtension, itype = Extension
i.name=text, itype = Narrative
i.name=basedOn, itype = Reference
i.name=bodySite, itype = CodeableConcept
i.name=category, itype = CodeableConcept
i.name=code, itype = CodeableConcept
i.name=component, itype = ObservationComponent
i.name=dataAbsentReason, itype = CodeableConcept
i.name=derivedFrom, itype = Reference
i.name=device, itype = Reference
i.name=effectiveDateTime
i.name=effectiveInstant
i.name=effectivePeriod, itype = Period
i.name=effectiveTiming, itype = Timing
i.name=encounter, itype = Reference
i.name=focus, itype = Reference
i.name=hasMember, itype = Reference
i.name=identifier, itype = Identifier
i.name=interpretation, itype = CodeableConcept
i.name=issued
i.name=method, itype = CodeableConcept
i.name=note, itype = Annotation
i.name=partOf, itype = Reference
i.name=performer, itype = Reference
i.name=referenceRange, itype = ObservationReferenceRange
i.name=specimen, itype = Reference
i.name=status
i.name=subject, itype = Reference
i.name=valueBoolean
i.name=valueCodeableConcept, itype = CodeableConcept
i.name=valueDateTime
i.name=valueInteger
i.name=valuePeriod, itype = Period
i.name=valueQuantity, itype = Quantity
i.name=valueRange, itype = Range
i.name=valueRatio, itype = Ratio
i.name=valueSampledData, itype = SampledData
i.name=valueString
i.name=valueTime
```

Is there an easier way?

Support for FastAPI pydantic response models

FastAPI supports using pydantic models to define JSON response bodies for REST API endpoints; making FHIR resources compatible with this support would take the usability of the library to a higher level.

I'm new to the library so I don't know more about how it uses pydantic other than what is stated in the README; in any case, however pydantic is currently used, it doesn't seem to be compatible on first look. The only requirement for compatibility with FastAPI is that the response model class is derived from pydantic's BaseModel.

A super basic example using the Practitioner resource from release 5.1.1, with FastAPI 0.62.0 and pydantic 1.7.3, is as follows:

from fastapi import APIRouter
from fhir.resources.practitioner import Practitioner

router = APIRouter()

@router.get(
    "/practitioner",
    response_model=Practitioner,
)
def get_practitioner(
) -> Any:
    return Practitioner()

This gives a couple of runtime errors on endpoint setup:

RuntimeError: no validator found for <class 'fhir.resources.practitioner.Practitioner'>, see `arbitrary_types_allowed` in Config
Invalid args for response field! Hint: check that <class 'fhir.resources.practitioner.Practitioner'> is a valid pydantic field type

This makes some sense, as the Practitioner resource (and most other resources, it would seem) derive from FHIRAbstractBase, and not pydantic's BaseModel.

Is FastAPI support something that's been considered already? Am I missing anything obvious regarding the use of pydantic?

DSTU2 Specimen resource has invalid fields

  • fhir.resources version: 6.0.0
  • Python version: 3.8
  • Operating System: MacOS

Description

While writing tests I found a bug in Specimen resource. It looks like his backbone elements are not defined correctly. I'm not sure what I am missing.
A result for example:

E   pydantic.error_wrappers.ValidationError: 3 validation errors for Specimen                           
E   collection                                                                                                                                                                                                  
E      (type=value_error)                                                                               
E   treatment -> 0                                                                                                                                                                                              
E      (type=value_error)                                                                               
E   container -> 0                                                                                      
E      (type=value_error)   

You can run the tests in the following branch:
https://github.com/ItayGoren/fhir.resources/tree/bug-with-specimen

Thanks

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.