Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
be1f0f5
Refactor ElementDefinition backbone models imports and clean up redun…
luisfabib Jan 16, 2026
49d70bf
Set extra configuration to "forbid" in FHIRBaseModel for stricter val…
luisfabib Jan 16, 2026
4f0db95
Refactor FHIR StructureDefinition handling to support multiple versions
luisfabib Jan 16, 2026
cf0377a
(WIP) start refactoring tests
luisfabib Jan 16, 2026
55d3a21
Fix FHIRPath `Union` Operation `TypeError` with Incomparable Types (#…
luisfabib Jan 17, 2026
aa7b74b
Fix FHIRPath Ambiguity Issues Where Negative Numbers Conflicted With …
luisfabib Jan 17, 2026
7122e46
Fix FHIRPath environment variable precedence in nested evaluations (#…
luisfabib Jan 17, 2026
786576e
Fix FHIR `eld-11` constraint validation expression R5 `ElementDefinit…
luisfabib Jan 17, 2026
ae29d61
Refactor FHIR constraint validators from field-level to model-level v…
luisfabib Jan 19, 2026
9fe5de6
(WIP) keep adapting and fixing unit tests for the new internal API
luisfabib Jan 19, 2026
3e417bf
Fix FHIRPath `$this` context in nested function call (#203)
luisfabib Jan 19, 2026
1e9e488
(WIP) update unit tests
luisfabib Jan 20, 2026
0ef557e
fix resource module units test and factory changes
luisfabib Jan 20, 2026
dbeaca8
remove versionless dirty structure and element defintion models
luisfabib Jan 20, 2026
12959fe
Add placeholder elements for `Extension.value` type choice values in …
luisfabib Jan 20, 2026
2cb35b9
Fix Primitive Extension Placeholder Cardinality for FHIR List-Type Fi…
luisfabib Jan 20, 2026
a5fad6b
Refactor test cases for improved readability and consistency
luisfabib Jan 22, 2026
ca55df2
fix new versioned structure definition issues in mapper module
luisfabib Jan 23, 2026
9fe3d07
fix documentation example
luisfabib Jan 23, 2026
5469379
accelerate integration tests by disabling validation of test artifact…
luisfabib Jan 23, 2026
7a77957
Squashed commit of the following:
luisfabib Jan 23, 2026
5d5bd54
Squashed commit of the following:
luisfabib Jan 23, 2026
82de105
remove unused files again
luisfabib Jan 23, 2026
134e298
Squashed commit of the following:
luisfabib Jan 23, 2026
9eeb4b0
Merge branch 'main' into fix-factory-versioned-sds
luisfabib Jan 23, 2026
eb0fc07
remove unusued files
luisfabib Jan 23, 2026
c7992b5
fix eld-19 and eld-20 FHIRPath strings for proper enconding. Fixes #209
luisfabib Jan 23, 2026
558fdf1
fix minor test issue
luisfabib Jan 23, 2026
b77d9d5
remove test for adding structure definition without version
luisfabib Jan 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions docs/user-guide/resources-construction.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,23 +37,27 @@ snapshot_structure_def = {
"type": "Resource",
"url": "http://example.org/fhir/StructureDefinition/LegacyPatient",
"fhirVersion": "4.0.1",
"kind": "resource",
"kind": "logical",
"status": "draft",
"abstract": False,
"abstract": True,
"snapshot": {
"element": [
{
"id": "LegacyPatient",
"path": "LegacyPatient",
"min": 0,
"max": "*"
"max": "*",
"definition": "A legacy patient",
"base": {"path": "Resource", "min": 0, "max": "*"},
},
{
"id": "LegacyPatient.fullName",
"path": "LegacyPatient.fullName",
"min": 1,
"max": "1",
"type": [{"code": "string"}]
"type": [{"code": "string"}],
"definition": "A legacy patient's full name",
"base": {"path": "Resource", "min": 0, "max": "*"},
}
]
}
Expand Down
26 changes: 12 additions & 14 deletions fhircraft/fhir/mapper/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@
from fhircraft.fhir.mapper.parser import FhirMappingLanguageParser
from fhircraft.fhir.resources.datatypes.R5.core.concept_map import ConceptMap
from fhircraft.fhir.resources.datatypes.R5.core.structure_map import StructureMap
from fhircraft.fhir.resources.repository import CompositeStructureDefinitionRepository
from fhircraft.fhir.resources.repository import (
CompositeStructureDefinitionRepository,
validate_structure_definition,
)

from .parser import FhirMappingLanguageParser

Expand Down Expand Up @@ -273,14 +276,8 @@ def add_structure_definition(
Raises:
ValueError: If structure definition is invalid or already exists
"""
from fhircraft.fhir.resources.definitions import StructureDefinition

if isinstance(structure_definition, dict):
struct_def = StructureDefinition(**structure_definition)
else:
struct_def = structure_definition

self.repository.add(struct_def, fail_if_exists=fail_if_exists)
structure_definition = validate_structure_definition(structure_definition)
self.repository.add(structure_definition, fail_if_exists=fail_if_exists)

def add_structure_definitions_from_file(
self, file_path: Union[str, Path], fail_if_exists: bool = False
Expand All @@ -299,7 +296,6 @@ def add_structure_definitions_from_file(
FileNotFoundError: If file doesn't exist
ValueError: If file format is invalid
"""
from fhircraft.fhir.resources.definitions import StructureDefinition

path = Path(file_path)
if not path.exists():
Expand All @@ -314,16 +310,18 @@ def add_structure_definitions_from_file(
if isinstance(data, dict):
if data.get("resourceType") == "StructureDefinition":
# Single StructureDefinition
struct_def = StructureDefinition(**data)
self.repository.add(struct_def, fail_if_exists=fail_if_exists)
structure_definition = validate_structure_definition(data)
self.repository.add(structure_definition, fail_if_exists=fail_if_exists)
count = 1
elif data.get("resourceType") == "Bundle" and data.get("entry"):
# Bundle containing StructureDefinitions
for entry in data["entry"]:
resource = entry.get("resource", {})
if resource.get("resourceType") == "StructureDefinition":
struct_def = StructureDefinition(**resource)
self.repository.add(struct_def, fail_if_exists=fail_if_exists)
structure_definition = validate_structure_definition(resource)
self.repository.add(
structure_definition, fail_if_exists=fail_if_exists
)
count += 1
else:
raise ValueError(
Expand Down
3 changes: 0 additions & 3 deletions fhircraft/fhir/resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
"""

from fhircraft.fhir.resources.base import FHIRBaseModel, FHIRSliceModel
from fhircraft.fhir.resources.definitions import ElementDefinition, StructureDefinition
from fhircraft.fhir.resources.factory import ResourceFactory, construct_resource_model
from fhircraft.fhir.resources.repository import (
CompositeStructureDefinitionRepository,
Expand All @@ -25,8 +24,6 @@
__all__ = [
"FHIRBaseModel",
"FHIRSliceModel",
"StructureDefinition",
"ElementDefinition",
"CompositeStructureDefinitionRepository",
"HttpStructureDefinitionRepository",
"PackageStructureDefinitionRepository",
Expand Down
30 changes: 27 additions & 3 deletions fhircraft/fhir/resources/datatypes/R4/complex/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,17 @@
from .timing import Timing
from .trigger_definition import TriggerDefinition
from .usage_context import UsageContext


from .element_definition import ElementDefinition
from .element_definition import (
ElementDefinition,
ElementDefinitionType,
ElementDefinitionBase,
ElementDefinitionBinding,
ElementDefinitionConstraint,
ElementDefinitionSlicing,
ElementDefinitionSlicingDiscriminator,
ElementDefinitionExample,
ElementDefinitionMapping,
)

__all__ = [
"Address",
Expand All @@ -73,6 +81,14 @@
"Dosage",
"Duration",
"Element",
"ElementDefinitionType",
"ElementDefinitionBase",
"ElementDefinitionBinding",
"ElementDefinitionConstraint",
"ElementDefinitionSlicing",
"ElementDefinitionSlicingDiscriminator",
"ElementDefinitionExample",
"ElementDefinitionMapping",
"ElementDefinition",
"Expression",
"Extension",
Expand Down Expand Up @@ -150,4 +166,12 @@
TriggerDefinition.model_rebuild()
UsageContext.model_rebuild()
ElementDefinition.model_rebuild()
ElementDefinitionType.model_rebuild()
ElementDefinitionBase.model_rebuild()
ElementDefinitionBinding.model_rebuild()
ElementDefinitionConstraint.model_rebuild()
ElementDefinitionSlicing.model_rebuild()
ElementDefinitionSlicingDiscriminator.model_rebuild()
ElementDefinitionExample.model_rebuild()
ElementDefinitionMapping.model_rebuild()
Extension.model_rebuild()
Original file line number Diff line number Diff line change
Expand Up @@ -2686,8 +2686,8 @@ def FHIR_eld_18_constraint_model_validator(self):
def FHIR_eld_19_constraint_model_validator(self):
return validate_model_constraint(
self,
expression="path.matches('[^\\s\\.,:;\\\\'\"\\/|?!@#$%&*()\\[\\]{}]{1,64}(\\.[^\\s\\.,:;\\\\'\"\\/|?!@#$%&*()\\[\\]{}]{1,64}(\\[x\\])?(\\:[^\\s\\.]+)?)*')",
human="Element names cannot include some special characters",
expression="""path.matches('^[^\\s\\.,:;\\\'"\\/|?!@#$%&*()\\[\\]{}]{1,64}(\\.[^\\s\\.,:;\\\'"\\/|?!@#$%&*()\\[\\]{}]{1,64}(\\[x\\])?(\\:[^\\s\\.]+)?)*$')""",
human="Element path SHALL be expressed as a set of '.'-separated components with each component restricted to a maximum of 64 characters and with some limits on the allowed choice of characters",
key="eld-19",
severity="error",
)
Expand All @@ -2696,8 +2696,8 @@ def FHIR_eld_19_constraint_model_validator(self):
def FHIR_eld_20_constraint_model_validator(self):
return validate_model_constraint(
self,
expression="path.matches('[A-Za-z][A-Za-z0-9]*(\\.[a-z][A-Za-z0-9]*(\\[x])?)*')",
human="Element names should be simple alphanumerics with a max of 64 characters, or code generation tools may be broken",
expression="""path.matches('^[A-Za-z][A-Za-z0-9](\\.[a-z][A-Za-z0-9](\\[x])?)*$')""",
human="The first component of the path should be UpperCamelCase. Additional components (following a '.') should be lowerCamelCase. If this syntax is not adhered to, code generation tools may be broken. Logical models may be less concerned about this implication.",
key="eld-20",
severity="warning",
)
Expand Down
28 changes: 27 additions & 1 deletion fhircraft/fhir/resources/datatypes/R4B/complex/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,17 @@
from .usage_context import UsageContext
from .population import Population
from .dosage import Dosage
from .element_definition import ElementDefinition
from .element_definition import (
ElementDefinition,
ElementDefinitionType,
ElementDefinitionBase,
ElementDefinitionBinding,
ElementDefinitionConstraint,
ElementDefinitionSlicing,
ElementDefinitionSlicingDiscriminator,
ElementDefinitionExample,
ElementDefinitionMapping,
)

__all__ = [
"Address",
Expand All @@ -73,6 +83,14 @@
"Dosage",
"Duration",
"Element",
"ElementDefinitionType",
"ElementDefinitionBase",
"ElementDefinitionBinding",
"ElementDefinitionConstraint",
"ElementDefinitionSlicing",
"ElementDefinitionSlicingDiscriminator",
"ElementDefinitionExample",
"ElementDefinitionMapping",
"ElementDefinition",
"Expression",
"Extension",
Expand Down Expand Up @@ -151,4 +169,12 @@
TriggerDefinition.model_rebuild()
UsageContext.model_rebuild()
ElementDefinition.model_rebuild()
ElementDefinitionType.model_rebuild()
ElementDefinitionBase.model_rebuild()
ElementDefinitionBinding.model_rebuild()
ElementDefinitionConstraint.model_rebuild()
ElementDefinitionSlicing.model_rebuild()
ElementDefinitionSlicingDiscriminator.model_rebuild()
ElementDefinitionExample.model_rebuild()
ElementDefinitionMapping.model_rebuild()
Extension.model_rebuild()
Original file line number Diff line number Diff line change
Expand Up @@ -2708,8 +2708,8 @@ def FHIR_eld_18_constraint_model_validator(self):
def FHIR_eld_19_constraint_model_validator(self):
return validate_model_constraint(
self,
expression="path.matches('^[^\\s\\.,:;\\\\'\"\\/|?!@#$%&*()\\[\\]{}]{1,64}(\\.[^\\s\\.,:;\\\\'\"\\/|?!@#$%&*()\\[\\]{}]{1,64}(\\[x\\])?(\\:[^\\s\\.]+)?)*$')",
human="Element names cannot include some special characters",
expression="""path.matches('^[^\\s\\.,:;\\\'"\\/|?!@#$%&*()\\[\\]{}]{1,64}(\\.[^\\s\\.,:;\\\'"\\/|?!@#$%&*()\\[\\]{}]{1,64}(\\[x\\])?(\\:[^\\s\\.]+)?)*$')""",
human="Element path SHALL be expressed as a set of '.'-separated components with each component restricted to a maximum of 64 characters and with some limits on the allowed choice of characters",
key="eld-19",
severity="error",
)
Expand All @@ -2718,8 +2718,8 @@ def FHIR_eld_19_constraint_model_validator(self):
def FHIR_eld_20_constraint_model_validator(self):
return validate_model_constraint(
self,
expression="path.matches('^[A-Za-z][A-Za-z0-9]*(\\.[a-z][A-Za-z0-9]*(\\[x])?)*$')",
human="Element names should be simple alphanumerics with a max of 64 characters, or code generation tools may be broken",
expression="""path.matches('^[A-Za-z][A-Za-z0-9](\\.[a-z][A-Za-z0-9](\\[x])?)*$')""",
human="The first component of the path should be UpperCamelCase. Additional components (following a '.') should be lowerCamelCase. If this syntax is not adhered to, code generation tools may be broken. Logical models may be less concerned about this implication.",
key="eld-20",
severity="warning",
)
Expand Down
31 changes: 30 additions & 1 deletion fhircraft/fhir/resources/datatypes/R5/complex/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,18 @@
from .extended_contact_detail import ExtendedContactDetail
from .virtual_service_detail import VirtualServiceDetail
from .dosage import Dosage
from .element_definition import ElementDefinition
from .element_definition import (
ElementDefinition,
ElementDefinitionType,
ElementDefinitionBase,
ElementDefinitionBinding,
ElementDefinitionBindingAdditional,
ElementDefinitionConstraint,
ElementDefinitionSlicing,
ElementDefinitionSlicingDiscriminator,
ElementDefinitionExample,
ElementDefinitionMapping,
)

__all__ = [
"Address",
Expand All @@ -83,6 +94,15 @@
"Dosage",
"Duration",
"Element",
"ElementDefinitionType",
"ElementDefinitionBase",
"ElementDefinitionBinding",
"ElementDefinitionBindingAdditional",
"ElementDefinitionConstraint",
"ElementDefinitionMapping",
"ElementDefinitionSlicing",
"ElementDefinitionSlicingDiscriminator",
"ElementDefinitionExample",
"ElementDefinition",
"Expression",
"ExtendedContactDetail",
Expand Down Expand Up @@ -169,4 +189,13 @@
UsageContext.model_rebuild()
VirtualServiceDetail.model_rebuild()
ElementDefinition.model_rebuild()
ElementDefinitionType.model_rebuild()
ElementDefinitionBase.model_rebuild()
ElementDefinitionBinding.model_rebuild()
ElementDefinitionBindingAdditional.model_rebuild()
ElementDefinitionConstraint.model_rebuild()
ElementDefinitionSlicing.model_rebuild()
ElementDefinitionSlicingDiscriminator.model_rebuild()
ElementDefinitionExample.model_rebuild()
ElementDefinitionMapping.model_rebuild()
Extension.model_rebuild()
Original file line number Diff line number Diff line change
Expand Up @@ -2930,7 +2930,7 @@ def FHIR_eld_18_constraint_model_validator(self):
def FHIR_eld_19_constraint_model_validator(self):
return validate_model_constraint(
self,
expression="path.matches('^[^\\s\\.,:;\\\\'\"\\/|?!@#$%&*()\\[\\]{}]{1,64}(\\.[^\\s\\.,:;\\\\'\"\\/|?!@#$%&*()\\[\\]{}]{1,64}(\\[x\\])?(\\:[^\\s\\.]+)?)*$')",
expression="""path.matches('^[^\\s\\.,:;\\\'"\\/|?!@#$%&*()\\[\\]{}]{1,64}(\\.[^\\s\\.,:;\\\'"\\/|?!@#$%&*()\\[\\]{}]{1,64}(\\[x\\])?(\\:[^\\s\\.]+)?)*$')""",
human="Element path SHALL be expressed as a set of '.'-separated components with each component restricted to a maximum of 64 characters and with some limits on the allowed choice of characters",
key="eld-19",
severity="error",
Expand All @@ -2940,7 +2940,7 @@ def FHIR_eld_19_constraint_model_validator(self):
def FHIR_eld_20_constraint_model_validator(self):
return validate_model_constraint(
self,
expression="path.matches('^[A-Za-z][A-Za-z0-9]{0,63}(\\.[a-z][A-Za-z0-9]{0,63}(\\[x])?)*$')",
expression="""path.matches('^[A-Za-z][A-Za-z0-9]{0,63}(\\.[a-z][A-Za-z0-9]{0,63}(\\[x])?)*$')""",
human="The first component of the path should be UpperCamelCase. Additional components (following a '.') should be lowerCamelCase. If this syntax is not adhered to, code generation tools may be broken. Logical models may be less concerned about this implication.",
key="eld-20",
severity="warning",
Expand Down
2 changes: 0 additions & 2 deletions fhircraft/fhir/resources/definitions/__init__.py

This file was deleted.

Loading