Skip to content

Builders API Reference

Document and common builders.

Document Builder

ClinicalDocument

Bases: CDAElement

Top-level C-CDA Clinical Document builder.

Functions

__init__(patient, author, custodian, sections=None, document_id=None, title='Clinical Summary', effective_time=None, **kwargs)

Initialize ClinicalDocument builder.

Parameters:

Name Type Description Default
patient PatientProtocol

Patient data satisfying PatientProtocol

required
author AuthorProtocol

Author data satisfying AuthorProtocol

required
custodian OrganizationProtocol

Custodian organization data satisfying OrganizationProtocol

required
sections Optional[Sequence[CDAElement]]

List of section builders (optional)

None
document_id Optional[str]

Document UUID (generated if not provided)

None
title str

Document title

'Clinical Summary'
effective_time Optional[datetime]

Document creation time (current time if not provided)

None
**kwargs

Additional arguments passed to CDAElement

{}

build()

Build ClinicalDocument XML element.

Returns:

Type Description
Element

lxml Element for ClinicalDocument

to_xml_string(pretty=True)

Convert to XML string with declaration.

Parameters:

Name Type Description Default
pretty bool

Whether to pretty-print XML

True

Returns:

Type Description
str

Complete XML document with declaration

Common Builders

Code

Bases: CDAElement

Reusable code element builder.

Source code in ccdakit/builders/common.py
class Code(CDAElement):
    """Reusable code element builder."""

    # Standard code system OIDs
    SYSTEM_OIDS = {
        # Clinical terminology systems
        "LOINC": "2.16.840.1.113883.6.1",
        "SNOMED": "2.16.840.1.113883.6.96",
        "RxNorm": "2.16.840.1.113883.6.88",
        "ICD-10": "2.16.840.1.113883.6.90",
        "ICD-10-CM": "2.16.840.1.113883.6.90",
        "ICD-10-PCS": "2.16.840.1.113883.6.4",
        "ICD-9-CM": "2.16.840.1.113883.6.103",
        "ICD-9-PCS": "2.16.840.1.113883.6.104",
        "CPT": "2.16.840.1.113883.6.12",
        "CVX": "2.16.840.1.113883.12.292",
        "NDC": "2.16.840.1.113883.6.69",
        "HCPCS": "2.16.840.1.113883.6.285",
        "NCI": "2.16.840.1.113883.3.26.1.1",
        "UNII": "2.16.840.1.113883.4.9",
        # Units of measure
        "UCUM": "2.16.840.1.113883.6.8",
        # HL7 vocabulary systems
        "HL7": "2.16.840.1.113883.5.1",
        "ActClass": "2.16.840.1.113883.5.6",
        "ActCode": "2.16.840.1.113883.5.4",
        "ActMood": "2.16.840.1.113883.5.1001",
        "ActStatus": "2.16.840.1.113883.5.14",
        "ObservationInterpretation": "2.16.840.1.113883.5.83",
        "ParticipationType": "2.16.840.1.113883.5.90",
        "RoleClass": "2.16.840.1.113883.5.110",
        "EntityNameUse": "2.16.840.1.113883.5.45",
        "PostalAddressUse": "2.16.840.1.113883.5.1119",
        "TelecomAddressUse": "2.16.840.1.113883.5.1119",
        "MaritalStatus": "2.16.840.1.113883.5.2",
        "ReligiousAffiliation": "2.16.840.1.113883.5.1076",
        "AdministrativeGender": "2.16.840.1.113883.5.1",
        "NullFlavor": "2.16.840.1.113883.5.1008",
        # CDC and demographic systems
        "Race": "2.16.840.1.113883.6.238",
        "Ethnicity": "2.16.840.1.113883.6.238",
        # International standards
        "Language": "2.16.840.1.113883.6.121",
        "ISO3166": "1.0.3166.1.2.2",
        # Healthcare facility and billing
        "NUBC": "2.16.840.1.113883.6.301",
        "DischargeDisposition": "2.16.840.1.113883.12.112",
        "AdmitSource": "2.16.840.1.113883.12.23",
        "ProcedureCode": "2.16.840.1.113883.6.96",
        # Additional HL7 vocabulary
        "RouteOfAdministration": "2.16.840.1.113883.5.112",
        "DoseForm": "2.16.840.1.113883.3.26.1.1",
        "BodySite": "2.16.840.1.113883.6.96",
        "Confidentiality": "2.16.840.1.113883.5.25",
        "EncounterType": "2.16.840.1.113883.5.4",
        "ProblemType": "2.16.840.1.113883.3.88.12.3221.7.2",
        "AllergyCategory": "2.16.840.1.113883.3.88.12.3221.6.2",
        "AllergySeverity": "2.16.840.1.113883.6.96",
        "ReactionSeverity": "2.16.840.1.113883.6.96",
        "MedicationStatus": "2.16.840.1.113883.3.88.12.80.20",
        "VitalSignResult": "2.16.840.1.113883.6.1",
        "LabResultStatus": "2.16.840.1.113883.5.14",
        "ResultInterpretation": "2.16.840.1.113883.5.83",
        "SpecimenType": "2.16.840.1.113883.6.96",
    }

    def __init__(
        self,
        code: Optional[str] = None,
        system: Optional[str] = None,
        display_name: Optional[str] = None,
        null_flavor: Optional[str] = None,
        **kwargs,
    ):
        """
        Initialize Code builder.

        Args:
            code: Code value
            system: Code system (name or OID)
            display_name: Human-readable display name
            null_flavor: Null flavor if code is not available
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(**kwargs)
        self.code = code
        self.system = system
        self.display_name = display_name
        self.null_flavor = null_flavor

    def build(self) -> etree.Element:
        """
        Build code XML element.

        Returns:
            lxml Element for code

        Raises:
            ValueError: If both code and null_flavor are missing
        """
        elem = etree.Element(f"{{{NS}}}code")

        if self.null_flavor:
            elem.set("nullFlavor", self.null_flavor)
        else:
            if not self.code or not self.system:
                raise ValueError("code and system required when null_flavor not provided")

            elem.set("code", self.code)

            # Handle system OID lookup
            if self.system in self.SYSTEM_OIDS:
                elem.set("codeSystem", self.SYSTEM_OIDS[self.system])
                elem.set("codeSystemName", self.system)
            else:
                elem.set("codeSystem", self.system)

            if self.display_name:
                elem.set("displayName", self.display_name)

        return elem

Functions

__init__(code=None, system=None, display_name=None, null_flavor=None, **kwargs)

Initialize Code builder.

Parameters:

Name Type Description Default
code Optional[str]

Code value

None
system Optional[str]

Code system (name or OID)

None
display_name Optional[str]

Human-readable display name

None
null_flavor Optional[str]

Null flavor if code is not available

None
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/common.py
def __init__(
    self,
    code: Optional[str] = None,
    system: Optional[str] = None,
    display_name: Optional[str] = None,
    null_flavor: Optional[str] = None,
    **kwargs,
):
    """
    Initialize Code builder.

    Args:
        code: Code value
        system: Code system (name or OID)
        display_name: Human-readable display name
        null_flavor: Null flavor if code is not available
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(**kwargs)
    self.code = code
    self.system = system
    self.display_name = display_name
    self.null_flavor = null_flavor

build()

Build code XML element.

Returns:

Type Description
Element

lxml Element for code

Raises:

Type Description
ValueError

If both code and null_flavor are missing

Source code in ccdakit/builders/common.py
def build(self) -> etree.Element:
    """
    Build code XML element.

    Returns:
        lxml Element for code

    Raises:
        ValueError: If both code and null_flavor are missing
    """
    elem = etree.Element(f"{{{NS}}}code")

    if self.null_flavor:
        elem.set("nullFlavor", self.null_flavor)
    else:
        if not self.code or not self.system:
            raise ValueError("code and system required when null_flavor not provided")

        elem.set("code", self.code)

        # Handle system OID lookup
        if self.system in self.SYSTEM_OIDS:
            elem.set("codeSystem", self.SYSTEM_OIDS[self.system])
            elem.set("codeSystemName", self.system)
        else:
            elem.set("codeSystem", self.system)

        if self.display_name:
            elem.set("displayName", self.display_name)

    return elem

EffectiveTime

Bases: CDAElement

Reusable effectiveTime element with support for points and intervals.

Source code in ccdakit/builders/common.py
class EffectiveTime(CDAElement):
    """Reusable effectiveTime element with support for points and intervals."""

    def __init__(
        self,
        value: Optional[datetime] = None,
        low: Optional[datetime] = None,
        high: Optional[datetime] = None,
        null_flavor: Optional[str] = None,
        **kwargs,
    ):
        """
        Initialize EffectiveTime builder.

        Args:
            value: Point in time
            low: Start of interval
            high: End of interval
            null_flavor: Null flavor if time is not available
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(**kwargs)
        self.value = value
        self.low = low
        self.high = high
        self.null_flavor = null_flavor

    def build(self) -> etree.Element:
        """
        Build effectiveTime XML element.

        Returns:
            lxml Element for effectiveTime
        """
        elem = etree.Element(f"{{{NS}}}effectiveTime")

        if self.null_flavor:
            elem.set("nullFlavor", self.null_flavor)
        elif self.value:
            # Point in time
            elem.set("value", self._format_datetime(self.value))
        else:
            # Interval
            if self.low:
                low = etree.SubElement(elem, f"{{{NS}}}low")
                low.set("value", self._format_datetime(self.low))

            if self.high:
                high = etree.SubElement(elem, f"{{{NS}}}high")
                high.set("value", self._format_datetime(self.high))
            elif self.low and not self.high:
                # Ongoing - use nullFlavor for high
                high = etree.SubElement(elem, f"{{{NS}}}high")
                high.set("nullFlavor", "UNK")

        return elem

    @staticmethod
    def _format_datetime(dt: datetime) -> str:
        """
        Format datetime to CDA format: YYYYMMDDHHMMSS.

        Args:
            dt: datetime or date object

        Returns:
            Formatted string
        """
        if isinstance(dt, date) and not isinstance(dt, datetime):
            # Date only
            return dt.strftime("%Y%m%d")
        # Full datetime with precision
        return dt.strftime("%Y%m%d%H%M%S")

Functions

__init__(value=None, low=None, high=None, null_flavor=None, **kwargs)

Initialize EffectiveTime builder.

Parameters:

Name Type Description Default
value Optional[datetime]

Point in time

None
low Optional[datetime]

Start of interval

None
high Optional[datetime]

End of interval

None
null_flavor Optional[str]

Null flavor if time is not available

None
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/common.py
def __init__(
    self,
    value: Optional[datetime] = None,
    low: Optional[datetime] = None,
    high: Optional[datetime] = None,
    null_flavor: Optional[str] = None,
    **kwargs,
):
    """
    Initialize EffectiveTime builder.

    Args:
        value: Point in time
        low: Start of interval
        high: End of interval
        null_flavor: Null flavor if time is not available
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(**kwargs)
    self.value = value
    self.low = low
    self.high = high
    self.null_flavor = null_flavor

build()

Build effectiveTime XML element.

Returns:

Type Description
Element

lxml Element for effectiveTime

Source code in ccdakit/builders/common.py
def build(self) -> etree.Element:
    """
    Build effectiveTime XML element.

    Returns:
        lxml Element for effectiveTime
    """
    elem = etree.Element(f"{{{NS}}}effectiveTime")

    if self.null_flavor:
        elem.set("nullFlavor", self.null_flavor)
    elif self.value:
        # Point in time
        elem.set("value", self._format_datetime(self.value))
    else:
        # Interval
        if self.low:
            low = etree.SubElement(elem, f"{{{NS}}}low")
            low.set("value", self._format_datetime(self.low))

        if self.high:
            high = etree.SubElement(elem, f"{{{NS}}}high")
            high.set("value", self._format_datetime(self.high))
        elif self.low and not self.high:
            # Ongoing - use nullFlavor for high
            high = etree.SubElement(elem, f"{{{NS}}}high")
            high.set("nullFlavor", "UNK")

    return elem

Identifier

Bases: CDAElement

Reusable ID element builder.

Source code in ccdakit/builders/common.py
class Identifier(CDAElement):
    """Reusable ID element builder."""

    def __init__(
        self,
        root: str,
        extension: Optional[str] = None,
        null_flavor: Optional[str] = None,
        **kwargs,
    ):
        """
        Initialize Identifier builder.

        Args:
            root: OID or UUID root
            extension: Extension within the root namespace
            null_flavor: Null flavor if ID is not available
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(**kwargs)
        self.root = root
        self.extension = extension
        self.null_flavor = null_flavor

    def build(self) -> etree.Element:
        """
        Build id XML element.

        Returns:
            lxml Element for id
        """
        elem = etree.Element(f"{{{NS}}}id")

        if self.null_flavor:
            elem.set("nullFlavor", self.null_flavor)
        else:
            elem.set("root", self.root)
            if self.extension:
                elem.set("extension", self.extension)

        return elem

Functions

__init__(root, extension=None, null_flavor=None, **kwargs)

Initialize Identifier builder.

Parameters:

Name Type Description Default
root str

OID or UUID root

required
extension Optional[str]

Extension within the root namespace

None
null_flavor Optional[str]

Null flavor if ID is not available

None
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/common.py
def __init__(
    self,
    root: str,
    extension: Optional[str] = None,
    null_flavor: Optional[str] = None,
    **kwargs,
):
    """
    Initialize Identifier builder.

    Args:
        root: OID or UUID root
        extension: Extension within the root namespace
        null_flavor: Null flavor if ID is not available
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(**kwargs)
    self.root = root
    self.extension = extension
    self.null_flavor = null_flavor

build()

Build id XML element.

Returns:

Type Description
Element

lxml Element for id

Source code in ccdakit/builders/common.py
def build(self) -> etree.Element:
    """
    Build id XML element.

    Returns:
        lxml Element for id
    """
    elem = etree.Element(f"{{{NS}}}id")

    if self.null_flavor:
        elem.set("nullFlavor", self.null_flavor)
    else:
        elem.set("root", self.root)
        if self.extension:
            elem.set("extension", self.extension)

    return elem

StatusCode

Bases: CDAElement

Reusable statusCode element builder.

Source code in ccdakit/builders/common.py
class StatusCode(CDAElement):
    """Reusable statusCode element builder."""

    def __init__(self, code: str, **kwargs):
        """
        Initialize StatusCode builder.

        Args:
            code: Status code value (e.g., 'completed', 'active')
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(**kwargs)
        self.code = code

    def build(self) -> etree.Element:
        """
        Build statusCode XML element.

        Returns:
            lxml Element for statusCode
        """
        elem = etree.Element(f"{{{NS}}}statusCode")
        elem.set("code", self.code)
        return elem

Functions

__init__(code, **kwargs)

Initialize StatusCode builder.

Parameters:

Name Type Description Default
code str

Status code value (e.g., 'completed', 'active')

required
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/common.py
def __init__(self, code: str, **kwargs):
    """
    Initialize StatusCode builder.

    Args:
        code: Status code value (e.g., 'completed', 'active')
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(**kwargs)
    self.code = code

build()

Build statusCode XML element.

Returns:

Type Description
Element

lxml Element for statusCode

Source code in ccdakit/builders/common.py
def build(self) -> etree.Element:
    """
    Build statusCode XML element.

    Returns:
        lxml Element for statusCode
    """
    elem = etree.Element(f"{{{NS}}}statusCode")
    elem.set("code", self.code)
    return elem

Demographic Builders

Address

Bases: CDAElement

Builder for CDA address elements.

Source code in ccdakit/builders/demographics.py
class Address(CDAElement):
    """Builder for CDA address elements."""

    # HL7 AddressUse codes
    USE_CODES = {
        "home": "HP",  # Primary home
        "work": "WP",  # Work place
        "temp": "TMP",  # Temporary
        "old": "OLD",  # No longer in use
        "physical": "PHYS",  # Physical visit address
        "postal": "PST",  # Postal address
    }

    def __init__(self, address: AddressProtocol, use: str = None, **kwargs):
        """
        Initialize Address builder.

        Args:
            address: Address data satisfying AddressProtocol
            use: Use code ('home', 'work', etc.) or HL7 code directly
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(**kwargs)
        self.address = address
        self.use = use

    def build(self) -> etree.Element:
        """
        Build address XML element.

        Returns:
            lxml Element for addr
        """
        elem = etree.Element(f"{{{NS}}}addr")

        # Add use attribute if specified
        if self.use:
            use_code = self.USE_CODES.get(self.use, self.use)
            elem.set("use", use_code)

        # Add street lines
        for line in self.address.street_lines:
            street = etree.SubElement(elem, f"{{{NS}}}streetAddressLine")
            street.text = line

        # Add city
        city = etree.SubElement(elem, f"{{{NS}}}city")
        city.text = self.address.city

        # Add state
        state = etree.SubElement(elem, f"{{{NS}}}state")
        state.text = self.address.state

        # Add postal code
        postal = etree.SubElement(elem, f"{{{NS}}}postalCode")
        postal.text = self.address.postal_code

        # Add country
        country = etree.SubElement(elem, f"{{{NS}}}country")
        country.text = self.address.country

        return elem

Functions

__init__(address, use=None, **kwargs)

Initialize Address builder.

Parameters:

Name Type Description Default
address AddressProtocol

Address data satisfying AddressProtocol

required
use str

Use code ('home', 'work', etc.) or HL7 code directly

None
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/demographics.py
def __init__(self, address: AddressProtocol, use: str = None, **kwargs):
    """
    Initialize Address builder.

    Args:
        address: Address data satisfying AddressProtocol
        use: Use code ('home', 'work', etc.) or HL7 code directly
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(**kwargs)
    self.address = address
    self.use = use

build()

Build address XML element.

Returns:

Type Description
Element

lxml Element for addr

Source code in ccdakit/builders/demographics.py
def build(self) -> etree.Element:
    """
    Build address XML element.

    Returns:
        lxml Element for addr
    """
    elem = etree.Element(f"{{{NS}}}addr")

    # Add use attribute if specified
    if self.use:
        use_code = self.USE_CODES.get(self.use, self.use)
        elem.set("use", use_code)

    # Add street lines
    for line in self.address.street_lines:
        street = etree.SubElement(elem, f"{{{NS}}}streetAddressLine")
        street.text = line

    # Add city
    city = etree.SubElement(elem, f"{{{NS}}}city")
    city.text = self.address.city

    # Add state
    state = etree.SubElement(elem, f"{{{NS}}}state")
    state.text = self.address.state

    # Add postal code
    postal = etree.SubElement(elem, f"{{{NS}}}postalCode")
    postal.text = self.address.postal_code

    # Add country
    country = etree.SubElement(elem, f"{{{NS}}}country")
    country.text = self.address.country

    return elem

Telecom

Bases: CDAElement

Builder for CDA telecom (contact) elements.

Source code in ccdakit/builders/demographics.py
class Telecom(CDAElement):
    """Builder for CDA telecom (contact) elements."""

    # HL7 TelecomUse codes
    USE_CODES = {
        "home": "HP",  # Primary home
        "work": "WP",  # Work place
        "mobile": "MC",  # Mobile contact
        "emergency": "EC",  # Emergency contact
    }

    def __init__(self, telecom: TelecomProtocol, **kwargs):
        """
        Initialize Telecom builder.

        Args:
            telecom: Telecom data satisfying TelecomProtocol
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(**kwargs)
        self.telecom = telecom

    def build(self) -> etree.Element:
        """
        Build telecom XML element.

        Returns:
            lxml Element for telecom
        """
        elem = etree.Element(f"{{{NS}}}telecom")

        # Build value based on type
        value = self._build_value()
        elem.set("value", value)

        # Add use attribute if specified
        if self.telecom.use:
            use_code = self.USE_CODES.get(self.telecom.use, self.telecom.use)
            elem.set("use", use_code)

        return elem

    def _build_value(self) -> str:
        """
        Build the value attribute based on telecom type.

        Returns:
            Formatted value string (e.g., 'tel:+1-617-555-1234', 'mailto:[email protected]')
        """
        telecom_type = self.telecom.type.lower()
        value = self.telecom.value

        if telecom_type == "phone":
            # Format: tel:+1-555-555-5555
            return f"tel:{value}"
        elif telecom_type == "fax":
            # Format: fax:+1-555-555-5555
            return f"tel:{value}"
        elif telecom_type == "email":
            # Format: mailto:[email protected]
            return f"mailto:{value}"
        elif telecom_type == "url":
            # Format: http://example.com or https://example.com
            if not value.startswith(("http://", "https://")):
                return f"http://{value}"
            return value
        else:
            # Default: return as-is
            return value

Functions

__init__(telecom, **kwargs)

Initialize Telecom builder.

Parameters:

Name Type Description Default
telecom TelecomProtocol

Telecom data satisfying TelecomProtocol

required
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/demographics.py
def __init__(self, telecom: TelecomProtocol, **kwargs):
    """
    Initialize Telecom builder.

    Args:
        telecom: Telecom data satisfying TelecomProtocol
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(**kwargs)
    self.telecom = telecom

build()

Build telecom XML element.

Returns:

Type Description
Element

lxml Element for telecom

Source code in ccdakit/builders/demographics.py
def build(self) -> etree.Element:
    """
    Build telecom XML element.

    Returns:
        lxml Element for telecom
    """
    elem = etree.Element(f"{{{NS}}}telecom")

    # Build value based on type
    value = self._build_value()
    elem.set("value", value)

    # Add use attribute if specified
    if self.telecom.use:
        use_code = self.USE_CODES.get(self.telecom.use, self.telecom.use)
        elem.set("use", use_code)

    return elem

Header Builders

Header builders are used internally by the ClinicalDocument builder.

See the ClinicalDocument API for usage.