Skip to content

Sections API Reference

All clinical section builders.

AdmissionDiagnosisSection

AdmissionDiagnosisSection

Bases: CDAElement

Builder for C-CDA Admission Diagnosis Section (V3).

This section contains a narrative description of the problems or diagnoses identified by the clinician at the time of the patient's admission. This section may contain a coded entry which represents the admitting diagnoses.

Template ID: 2.16.840.1.113883.10.20.22.2.43 (V3) Supports R2.1 (2015-08-01) version.

The section includes: - Narrative table showing diagnoses - Hospital Admission Diagnosis entries with Problem Observations

Conformance Requirements: - SHALL contain templateId (CONF:1198-9930, 1198-10391, 1198-32563) - SHALL contain code 46241-6 with translation 42347-5 (CONF:1198-15479, etc.) - SHALL contain title (CONF:1198-9932) - SHALL contain text (CONF:1198-9933) - SHOULD contain entry with Hospital Admission Diagnosis (CONF:1198-9934, 1198-15481)

Source code in ccdakit/builders/sections/admission_diagnosis.py
class AdmissionDiagnosisSection(CDAElement):
    """
    Builder for C-CDA Admission Diagnosis Section (V3).

    This section contains a narrative description of the problems or diagnoses
    identified by the clinician at the time of the patient's admission. This
    section may contain a coded entry which represents the admitting diagnoses.

    Template ID: 2.16.840.1.113883.10.20.22.2.43 (V3)
    Supports R2.1 (2015-08-01) version.

    The section includes:
    - Narrative table showing diagnoses
    - Hospital Admission Diagnosis entries with Problem Observations

    Conformance Requirements:
    - SHALL contain templateId (CONF:1198-9930, 1198-10391, 1198-32563)
    - SHALL contain code 46241-6 with translation 42347-5 (CONF:1198-15479, etc.)
    - SHALL contain title (CONF:1198-9932)
    - SHALL contain text (CONF:1198-9933)
    - SHOULD contain entry with Hospital Admission Diagnosis (CONF:1198-9934, 1198-15481)
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.43",
                extension="2015-08-01",
                description="Admission Diagnosis Section (V3) R2.1",
            ),
        ],
    }

    def __init__(
        self,
        diagnoses: Optional[Sequence[AdmissionDiagnosisProtocol]] = None,
        title: str = "Hospital Admission Diagnosis",
        version: CDAVersion = CDAVersion.R2_1,
        **kwargs,
    ):
        """
        Initialize AdmissionDiagnosisSection builder.

        Args:
            diagnoses: List of admission diagnoses satisfying AdmissionDiagnosisProtocol
            title: Section title (default: "Hospital Admission Diagnosis")
            version: C-CDA version (R2.1)
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.diagnoses = diagnoses or []
        self.title = title

    def build(self) -> etree.Element:
        """
        Build Admission Diagnosis Section XML element.

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add template IDs (CONF:1198-9930, 1198-10391, 1198-32563)
        self.add_template_ids(section)

        # Add section code with translation (CONF:1198-15479, 1198-15480, 1198-30865)
        self._add_code(section)

        # Add title (CONF:1198-9932)
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (CONF:1198-9933)
        self._add_narrative(section)

        # Add entries (CONF:1198-9934, 1198-15481)
        for diagnosis in self.diagnoses:
            self._add_entry(section, diagnosis)

        return section

    def _add_code(self, section: etree._Element) -> None:
        """
        Add section code element with required translation.

        Conformance:
        - CONF:1198-15479: SHALL contain code
        - CONF:1198-15480: code SHALL be 46241-6
        - CONF:1198-30865: codeSystem SHALL be LOINC
        - CONF:1198-32749: SHALL contain translation
        - CONF:1198-32750: translation code SHALL be 42347-5
        - CONF:1198-32751: translation codeSystem SHALL be LOINC

        Args:
            section: section element
        """
        # Create primary code (CONF:1198-15479, 1198-15480, 1198-30865)
        code_elem = Code(
            code="46241-6",
            system="LOINC",
            display_name="Hospital Admission diagnosis",
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"

        # Add required translation (CONF:1198-32749, 1198-32750, 1198-32751)
        translation = etree.SubElement(code_elem, f"{{{NS}}}translation")
        translation.set("code", "42347-5")
        translation.set("codeSystem", "2.16.840.1.113883.6.1")
        translation.set("displayName", "Admission Diagnosis")

        section.append(code_elem)

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element with HTML table.

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        if not self.diagnoses:
            # No diagnoses - add "No admission diagnosis documented" paragraph
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            paragraph.text = "No admission diagnosis documented"
            return

        # Create table for diagnoses
        table = etree.SubElement(text, f"{{{NS}}}table", border="1", width="100%")

        # Table header
        thead = etree.SubElement(table, f"{{{NS}}}thead")
        tr = etree.SubElement(thead, f"{{{NS}}}tr")

        headers = ["Diagnosis", "Code", "Admission Date", "Diagnosis Date"]
        for header_text in headers:
            th = etree.SubElement(tr, f"{{{NS}}}th")
            th.text = header_text

        # Table body
        tbody = etree.SubElement(table, f"{{{NS}}}tbody")

        for idx, diagnosis in enumerate(self.diagnoses, start=1):
            tr = etree.SubElement(tbody, f"{{{NS}}}tr")

            # Diagnosis name (with ID reference)
            td_name = etree.SubElement(tr, f"{{{NS}}}td")
            content = etree.SubElement(
                td_name,
                f"{{{NS}}}content",
                ID=f"admission-diagnosis-{idx}",
            )
            content.text = diagnosis.name

            # Code
            td_code = etree.SubElement(tr, f"{{{NS}}}td")
            td_code.text = f"{diagnosis.code} ({diagnosis.code_system})"

            # Admission date
            td_admission = etree.SubElement(tr, f"{{{NS}}}td")
            if diagnosis.admission_date:
                td_admission.text = diagnosis.admission_date.strftime("%Y-%m-%d")
            else:
                td_admission.text = "Unknown"

            # Diagnosis date
            td_diagnosis = etree.SubElement(tr, f"{{{NS}}}td")
            if diagnosis.diagnosis_date:
                td_diagnosis.text = diagnosis.diagnosis_date.strftime("%Y-%m-%d")
            else:
                td_diagnosis.text = "Unknown"

    def _add_entry(self, section: etree._Element, diagnosis: AdmissionDiagnosisProtocol) -> None:
        """
        Add entry element with Hospital Admission Diagnosis act.

        Conformance:
        - CONF:1198-9934: SHOULD contain entry
        - CONF:1198-15481: entry SHALL contain Hospital Admission Diagnosis

        Args:
            section: section element
            diagnosis: Admission diagnosis data
        """
        # Create entry element (CONF:1198-9934)
        entry = etree.SubElement(section, f"{{{NS}}}entry")

        # Create and add Hospital Admission Diagnosis act (CONF:1198-15481)
        diagnosis_builder = HospitalAdmissionDiagnosis(diagnosis, version=self.version)
        entry.append(diagnosis_builder.to_element())

Functions

__init__(diagnoses=None, title='Hospital Admission Diagnosis', version=CDAVersion.R2_1, **kwargs)

Initialize AdmissionDiagnosisSection builder.

Parameters:

Name Type Description Default
diagnoses Optional[Sequence[AdmissionDiagnosisProtocol]]

List of admission diagnoses satisfying AdmissionDiagnosisProtocol

None
title str

Section title (default: "Hospital Admission Diagnosis")

'Hospital Admission Diagnosis'
version CDAVersion

C-CDA version (R2.1)

R2_1
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/admission_diagnosis.py
def __init__(
    self,
    diagnoses: Optional[Sequence[AdmissionDiagnosisProtocol]] = None,
    title: str = "Hospital Admission Diagnosis",
    version: CDAVersion = CDAVersion.R2_1,
    **kwargs,
):
    """
    Initialize AdmissionDiagnosisSection builder.

    Args:
        diagnoses: List of admission diagnoses satisfying AdmissionDiagnosisProtocol
        title: Section title (default: "Hospital Admission Diagnosis")
        version: C-CDA version (R2.1)
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.diagnoses = diagnoses or []
    self.title = title

build()

Build Admission Diagnosis Section XML element.

Returns:

Type Description
Element

lxml Element for section

Source code in ccdakit/builders/sections/admission_diagnosis.py
def build(self) -> etree.Element:
    """
    Build Admission Diagnosis Section XML element.

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add template IDs (CONF:1198-9930, 1198-10391, 1198-32563)
    self.add_template_ids(section)

    # Add section code with translation (CONF:1198-15479, 1198-15480, 1198-30865)
    self._add_code(section)

    # Add title (CONF:1198-9932)
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (CONF:1198-9933)
    self._add_narrative(section)

    # Add entries (CONF:1198-9934, 1198-15481)
    for diagnosis in self.diagnoses:
        self._add_entry(section, diagnosis)

    return section

AdmissionMedicationsSection

AdmissionMedicationsSection

Bases: CDAElement

Builder for C-CDA Admission Medications Section (entries optional).

The section contains the medications taken by the patient prior to and at the time of admission to the facility.

Template ID: 2.16.840.1.113883.10.20.22.2.44 Supports both R2.1 (2015-08-01) and R2.0 versions.

Source code in ccdakit/builders/sections/admission_medications.py
class AdmissionMedicationsSection(CDAElement):
    """
    Builder for C-CDA Admission Medications Section (entries optional).

    The section contains the medications taken by the patient prior to
    and at the time of admission to the facility.

    Template ID: 2.16.840.1.113883.10.20.22.2.44
    Supports both R2.1 (2015-08-01) and R2.0 versions.
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.44",
                extension="2015-08-01",
                description="Admission Medications Section (entries optional) V3",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.44",
                extension=None,  # R2.0 version may not have extension
                description="Admission Medications Section (entries optional)",
            ),
        ],
    }

    # LOINC code for Medications on Admission
    LOINC_CODE = "42346-7"
    LOINC_OID = "2.16.840.1.113883.6.1"

    def __init__(
        self,
        medications: Sequence[MedicationProtocol],
        title: str = "Admission Medications",
        version: CDAVersion = CDAVersion.R2_1,
        null_flavor: Optional[str] = None,
        **kwargs,
    ):
        """
        Initialize AdmissionMedicationsSection builder.

        Args:
            medications: List of medications satisfying MedicationProtocol
            title: Section title (default: "Admission Medications")
            version: C-CDA version (R2.1 or R2.0)
            null_flavor: Null flavor if no medications available (e.g., "NI" for no information)
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.medications = medications
        self.title = title
        self.null_flavor = null_flavor

    def build(self) -> etree.Element:
        """
        Build Admission Medications Section XML element.

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add template IDs
        # CONF:1198-10098, CONF:1198-10392, CONF:1198-32560
        self.add_template_ids(section)

        # Add section code
        # CONF:1198-15482, CONF:1198-15483, CONF:1198-32142
        code_elem = Code(
            code=self.LOINC_CODE,
            system="LOINC",
            display_name="Medications on Admission",
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # Add title
        # CONF:1198-10100
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (HTML table)
        # CONF:1198-10101
        self._add_narrative(section)

        # Add entries with Admission Medication acts
        # CONF:1198-10102 (SHOULD), CONF:1198-15484
        for medication in self.medications:
            self._add_entry(section, medication)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element with HTML table.

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        if self.null_flavor:
            # Handle null flavor case
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            null_flavor_text = {
                "NI": "No information about admission medications",
                "NA": "Not applicable",
                "UNK": "Unknown",
                "ASKU": "Asked but unknown",
                "NAV": "Temporarily unavailable",
                "NASK": "Not asked",
                "MSK": "Masked",
                "OTH": "Other",
            }
            paragraph.text = null_flavor_text.get(
                self.null_flavor, "No admission medications recorded"
            )
            return

        if not self.medications:
            # No medications - add "No medications on admission" paragraph
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            paragraph.text = "No medications on admission"
            return

        # Create table for medications
        table = etree.SubElement(text, f"{{{NS}}}table", border="1", width="100%")

        # Table header
        thead = etree.SubElement(table, f"{{{NS}}}thead")
        tr = etree.SubElement(thead, f"{{{NS}}}tr")

        headers = [
            "Medication",
            "Dosage",
            "Route",
            "Frequency",
            "Start Date",
            "End Date",
            "Status",
        ]
        for header_text in headers:
            th = etree.SubElement(tr, f"{{{NS}}}th")
            th.text = header_text

        # Table body
        tbody = etree.SubElement(table, f"{{{NS}}}tbody")

        for idx, medication in enumerate(self.medications, start=1):
            tr = etree.SubElement(tbody, f"{{{NS}}}tr")

            # Medication name (with ID reference)
            td_name = etree.SubElement(tr, f"{{{NS}}}td")
            content = etree.SubElement(
                td_name,
                f"{{{NS}}}content",
                ID=f"admission-medication-{idx}",
            )
            content.text = medication.name

            # Dosage
            td_dosage = etree.SubElement(tr, f"{{{NS}}}td")
            td_dosage.text = str(medication.dosage) if medication.dosage else "N/A"

            # Route
            td_route = etree.SubElement(tr, f"{{{NS}}}td")
            if medication.route:
                td_route.text = medication.route.capitalize()
            else:
                td_route.text = "N/A"

            # Frequency
            td_frequency = etree.SubElement(tr, f"{{{NS}}}td")
            td_frequency.text = str(medication.frequency) if medication.frequency else "N/A"

            # Start date
            td_start = etree.SubElement(tr, f"{{{NS}}}td")
            if medication.start_date:
                td_start.text = medication.start_date.strftime("%Y-%m-%d")
            else:
                td_start.text = "Unknown"

            # End date
            td_end = etree.SubElement(tr, f"{{{NS}}}td")
            if medication.end_date:
                td_end.text = medication.end_date.strftime("%Y-%m-%d")
            elif medication.status == "active":
                td_end.text = "Ongoing"
            else:
                td_end.text = "Unknown"

            # Status
            td_status = etree.SubElement(tr, f"{{{NS}}}td")
            if medication.status:
                td_status.text = medication.status.capitalize()
            else:
                td_status.text = "Unknown"

    def _add_entry(self, section: etree._Element, medication: MedicationProtocol) -> None:
        """
        Add entry element with Admission Medication.

        Args:
            section: section element
            medication: Medication data
        """
        # Create entry element
        entry = etree.SubElement(section, f"{{{NS}}}entry")

        # Create and add Admission Medication (act wrapper)
        admission_med = AdmissionMedication(medication, version=self.version)
        entry.append(admission_med.to_element())

Functions

__init__(medications, title='Admission Medications', version=CDAVersion.R2_1, null_flavor=None, **kwargs)

Initialize AdmissionMedicationsSection builder.

Parameters:

Name Type Description Default
medications Sequence[MedicationProtocol]

List of medications satisfying MedicationProtocol

required
title str

Section title (default: "Admission Medications")

'Admission Medications'
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
null_flavor Optional[str]

Null flavor if no medications available (e.g., "NI" for no information)

None
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/admission_medications.py
def __init__(
    self,
    medications: Sequence[MedicationProtocol],
    title: str = "Admission Medications",
    version: CDAVersion = CDAVersion.R2_1,
    null_flavor: Optional[str] = None,
    **kwargs,
):
    """
    Initialize AdmissionMedicationsSection builder.

    Args:
        medications: List of medications satisfying MedicationProtocol
        title: Section title (default: "Admission Medications")
        version: C-CDA version (R2.1 or R2.0)
        null_flavor: Null flavor if no medications available (e.g., "NI" for no information)
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.medications = medications
    self.title = title
    self.null_flavor = null_flavor

build()

Build Admission Medications Section XML element.

Returns:

Type Description
Element

lxml Element for section

Source code in ccdakit/builders/sections/admission_medications.py
def build(self) -> etree.Element:
    """
    Build Admission Medications Section XML element.

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add template IDs
    # CONF:1198-10098, CONF:1198-10392, CONF:1198-32560
    self.add_template_ids(section)

    # Add section code
    # CONF:1198-15482, CONF:1198-15483, CONF:1198-32142
    code_elem = Code(
        code=self.LOINC_CODE,
        system="LOINC",
        display_name="Medications on Admission",
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # Add title
    # CONF:1198-10100
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (HTML table)
    # CONF:1198-10101
    self._add_narrative(section)

    # Add entries with Admission Medication acts
    # CONF:1198-10102 (SHOULD), CONF:1198-15484
    for medication in self.medications:
        self._add_entry(section, medication)

    return section

AdvanceDirectivesSection

AdvanceDirectivesSection

Bases: CDAElement

Builder for C-CDA Advance Directives Section (entries required).

Contains advance directives data and references to supporting documentation, including living wills, healthcare proxies, and CPR/resuscitation status. Supports both R2.1 (2015-08-01) and R2.0 (2015-08-01) versions.

Template ID: 2.16.840.1.113883.10.20.22.2.21.1

Source code in ccdakit/builders/sections/advance_directives.py
class AdvanceDirectivesSection(CDAElement):
    """
    Builder for C-CDA Advance Directives Section (entries required).

    Contains advance directives data and references to supporting documentation,
    including living wills, healthcare proxies, and CPR/resuscitation status.
    Supports both R2.1 (2015-08-01) and R2.0 (2015-08-01) versions.

    Template ID: 2.16.840.1.113883.10.20.22.2.21.1
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.21.1",
                extension="2015-08-01",
                description="Advance Directives Section (entries required) R2.1",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.21.1",
                extension="2015-08-01",
                description="Advance Directives Section (entries required) R2.0",
            ),
        ],
    }

    def __init__(
        self,
        directives: Optional[Sequence[AdvanceDirectiveProtocol]] = None,
        title: str = "Advance Directives",
        null_flavor: Optional[str] = None,
        version: CDAVersion = CDAVersion.R2_1,
        **kwargs,
    ):
        """
        Initialize AdvanceDirectivesSection builder.

        Args:
            directives: List of advance directives (None or empty for no information)
            title: Section title (default: "Advance Directives")
            null_flavor: NullFlavor if no information available (e.g., "NI")
            version: C-CDA version (R2.1 or R2.0)
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.directives = directives if directives is not None else []
        self.title = title
        self.null_flavor = null_flavor

    def build(self) -> etree.Element:
        """
        Build Advance Directives Section XML element.

        Conformance rules implemented:
        - CONF:1198-32800: MAY contain @nullFlavor="NI"
        - CONF:1198-30227: SHALL contain templateId
        - CONF:1198-30228: templateId/@root="2.16.840.1.113883.10.20.22.2.21.1"
        - CONF:1198-32512: templateId/@extension="2015-08-01"
        - CONF:1198-32929: SHALL contain code
        - CONF:1198-32930: code/@code="42348-3"
        - CONF:1198-32931: code/@codeSystem=LOINC
        - CONF:1198-32932: SHALL contain title
        - CONF:1198-32933: SHALL contain text
        - CONF:1198-30235: If not nullFlavor, SHALL contain entry
        - CONF:1198-30236: entry MAY contain Advance Directive Observation
        - CONF:1198-32420: entry MAY contain Advance Directive Organizer
        - CONF:1198-32881: entry SHALL contain one or more entries with
                           EITHER observation OR organizer

        Returns:
            lxml Element for section
        """
        # Create section element (CONF:1198-32800)
        section = etree.Element(f"{{{NS}}}section")
        if self.null_flavor:
            section.set("nullFlavor", self.null_flavor)

        # Add template IDs (CONF:1198-30227, 30228, 32512)
        self.add_template_ids(section)

        # Add section code (CONF:1198-32929, 32930, 32931)
        code_elem = Code(
            code="42348-3",
            system="LOINC",
            display_name="Advance Directives",
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # Add title (CONF:1198-32932)
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (CONF:1198-32933)
        self._add_narrative(section)

        # Add entries if not nullFlavor (CONF:1198-30235, 30236, 32420, 32881)
        if not self.null_flavor:
            for directive in self.directives:
                self._add_entry(section, directive)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element with HTML table.

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        if self.null_flavor or not self.directives:
            # No information or no directives
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            if self.null_flavor == "NI":
                paragraph.text = "No information about advance directives"
            else:
                paragraph.text = "No advance directives on file"
            return

        # Create table for advance directives
        table = etree.SubElement(text, f"{{{NS}}}table", border="1", width="100%")

        # Table header
        thead = etree.SubElement(table, f"{{{NS}}}thead")
        tr = etree.SubElement(thead, f"{{{NS}}}tr")

        headers = [
            "Type",
            "Directive",
            "Start Date",
            "End Date",
            "Custodian",
            "Verification",
        ]
        for header_text in headers:
            th = etree.SubElement(tr, f"{{{NS}}}th")
            th.text = header_text

        # Table body
        tbody = etree.SubElement(table, f"{{{NS}}}tbody")

        for idx, directive in enumerate(self.directives, start=1):
            tr = etree.SubElement(tbody, f"{{{NS}}}tr")

            # Directive type (with ID reference)
            td_type = etree.SubElement(tr, f"{{{NS}}}td")
            content = etree.SubElement(
                td_type,
                f"{{{NS}}}content",
                ID=f"directive-{idx}",
            )
            content.text = directive.directive_type

            # Directive value/details
            td_value = etree.SubElement(tr, f"{{{NS}}}td")
            if directive.document_url:
                # Create link if URL available
                link = etree.SubElement(
                    td_value,
                    f"{{{NS}}}linkHtml",
                    href=directive.document_url,
                )
                link.text = directive.directive_value
            else:
                td_value.text = directive.directive_value

            # Start date
            td_start = etree.SubElement(tr, f"{{{NS}}}td")
            if directive.start_date:
                td_start.text = directive.start_date.strftime("%Y-%m-%d")
            else:
                td_start.text = "Unknown"

            # End date
            td_end = etree.SubElement(tr, f"{{{NS}}}td")
            if directive.end_date:
                td_end.text = directive.end_date.strftime("%Y-%m-%d")
            else:
                td_end.text = "N/A"

            # Custodian
            td_custodian = etree.SubElement(tr, f"{{{NS}}}td")
            if directive.custodian_name:
                custodian_text = directive.custodian_name
                if directive.custodian_relationship:
                    custodian_text += f" ({directive.custodian_relationship})"
                td_custodian.text = custodian_text
            else:
                td_custodian.text = "Not specified"

            # Verification
            td_verification = etree.SubElement(tr, f"{{{NS}}}td")
            if directive.verifier_name or directive.verification_date:
                verification_text = ""
                if directive.verifier_name:
                    verification_text = directive.verifier_name
                if directive.verification_date:
                    date_str = directive.verification_date.strftime("%Y-%m-%d")
                    verification_text += f" on {date_str}" if verification_text else date_str
                td_verification.text = verification_text
            else:
                td_verification.text = "Not verified"

    def _add_entry(self, section: etree._Element, directive: AdvanceDirectiveProtocol) -> None:
        """
        Add entry element with Advance Directive Observation.

        Note: This implementation creates individual observations.
        The spec also allows for Advance Directive Organizer (template
        2.16.840.1.113883.10.20.22.4.108) which groups multiple observations,
        but for simplicity we create individual observations per entry.

        Args:
            section: section element
            directive: Advance directive data
        """
        # Create entry element (CONF:1198-30235)
        entry = etree.SubElement(section, f"{{{NS}}}entry")

        # Create and add Advance Directive Observation (CONF:1198-30236)
        directive_builder = AdvanceDirectiveObservation(directive, version=self.version)
        entry.append(directive_builder.to_element())

Functions

__init__(directives=None, title='Advance Directives', null_flavor=None, version=CDAVersion.R2_1, **kwargs)

Initialize AdvanceDirectivesSection builder.

Parameters:

Name Type Description Default
directives Optional[Sequence[AdvanceDirectiveProtocol]]

List of advance directives (None or empty for no information)

None
title str

Section title (default: "Advance Directives")

'Advance Directives'
null_flavor Optional[str]

NullFlavor if no information available (e.g., "NI")

None
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/advance_directives.py
def __init__(
    self,
    directives: Optional[Sequence[AdvanceDirectiveProtocol]] = None,
    title: str = "Advance Directives",
    null_flavor: Optional[str] = None,
    version: CDAVersion = CDAVersion.R2_1,
    **kwargs,
):
    """
    Initialize AdvanceDirectivesSection builder.

    Args:
        directives: List of advance directives (None or empty for no information)
        title: Section title (default: "Advance Directives")
        null_flavor: NullFlavor if no information available (e.g., "NI")
        version: C-CDA version (R2.1 or R2.0)
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.directives = directives if directives is not None else []
    self.title = title
    self.null_flavor = null_flavor

build()

Build Advance Directives Section XML element.

Conformance rules implemented: - CONF:1198-32800: MAY contain @nullFlavor="NI" - CONF:1198-30227: SHALL contain templateId - CONF:1198-30228: templateId/@root="2.16.840.1.113883.10.20.22.2.21.1" - CONF:1198-32512: templateId/@extension="2015-08-01" - CONF:1198-32929: SHALL contain code - CONF:1198-32930: code/@code="42348-3" - CONF:1198-32931: code/@codeSystem=LOINC - CONF:1198-32932: SHALL contain title - CONF:1198-32933: SHALL contain text - CONF:1198-30235: If not nullFlavor, SHALL contain entry - CONF:1198-30236: entry MAY contain Advance Directive Observation - CONF:1198-32420: entry MAY contain Advance Directive Organizer - CONF:1198-32881: entry SHALL contain one or more entries with EITHER observation OR organizer

Returns:

Type Description
Element

lxml Element for section

Source code in ccdakit/builders/sections/advance_directives.py
def build(self) -> etree.Element:
    """
    Build Advance Directives Section XML element.

    Conformance rules implemented:
    - CONF:1198-32800: MAY contain @nullFlavor="NI"
    - CONF:1198-30227: SHALL contain templateId
    - CONF:1198-30228: templateId/@root="2.16.840.1.113883.10.20.22.2.21.1"
    - CONF:1198-32512: templateId/@extension="2015-08-01"
    - CONF:1198-32929: SHALL contain code
    - CONF:1198-32930: code/@code="42348-3"
    - CONF:1198-32931: code/@codeSystem=LOINC
    - CONF:1198-32932: SHALL contain title
    - CONF:1198-32933: SHALL contain text
    - CONF:1198-30235: If not nullFlavor, SHALL contain entry
    - CONF:1198-30236: entry MAY contain Advance Directive Observation
    - CONF:1198-32420: entry MAY contain Advance Directive Organizer
    - CONF:1198-32881: entry SHALL contain one or more entries with
                       EITHER observation OR organizer

    Returns:
        lxml Element for section
    """
    # Create section element (CONF:1198-32800)
    section = etree.Element(f"{{{NS}}}section")
    if self.null_flavor:
        section.set("nullFlavor", self.null_flavor)

    # Add template IDs (CONF:1198-30227, 30228, 32512)
    self.add_template_ids(section)

    # Add section code (CONF:1198-32929, 32930, 32931)
    code_elem = Code(
        code="42348-3",
        system="LOINC",
        display_name="Advance Directives",
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # Add title (CONF:1198-32932)
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (CONF:1198-32933)
    self._add_narrative(section)

    # Add entries if not nullFlavor (CONF:1198-30235, 30236, 32420, 32881)
    if not self.null_flavor:
        for directive in self.directives:
            self._add_entry(section, directive)

    return section

AllergiesSection

AllergiesSection

Bases: CDAElement

Builder for C-CDA Allergies and Intolerances Section.

Includes narrative (HTML table) and structured entries. Supports both R2.1 (2014-06-09) and R2.0 (2014-06-09) versions.

Source code in ccdakit/builders/sections/allergies.py
class AllergiesSection(CDAElement):
    """
    Builder for C-CDA Allergies and Intolerances Section.

    Includes narrative (HTML table) and structured entries.
    Supports both R2.1 (2014-06-09) and R2.0 (2014-06-09) versions.
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.6.1",
                extension="2015-08-01",
                description="Allergies and Intolerances Section (entries required) R2.1",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.6.1",
                extension="2015-08-01",
                description="Allergies and Intolerances Section (entries required) R2.0",
            ),
        ],
    }

    def __init__(
        self,
        allergies: Sequence[AllergyProtocol],
        title: str = "Allergies and Intolerances",
        version: CDAVersion = CDAVersion.R2_1,
        **kwargs,
    ):
        """
        Initialize AllergiesSection builder.

        Args:
            allergies: List of allergies satisfying AllergyProtocol
            title: Section title (default: "Allergies and Intolerances")
            version: C-CDA version (R2.1 or R2.0)
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.allergies = allergies
        self.title = title

    def build(self) -> etree.Element:
        """
        Build Allergies and Intolerances Section XML element.

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add template IDs
        self.add_template_ids(section)

        # Add section code (48765-2 = Allergies and adverse reactions Document)
        code_elem = Code(
            code="48765-2",
            system="LOINC",
            display_name="Allergies and adverse reactions Document",
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # Add title
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (HTML table)
        self._add_narrative(section)

        # Add entries with Allergy Concern Acts
        for allergy in self.allergies:
            self._add_entry(section, allergy)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element with HTML table.

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        if not self.allergies:
            # No allergies - add "No known allergies" paragraph
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            paragraph.text = "No known allergies"
            return

        # Create table for allergies
        table = etree.SubElement(text, f"{{{NS}}}table", border="1", width="100%")

        # Table header
        thead = etree.SubElement(table, f"{{{NS}}}thead")
        tr = etree.SubElement(thead, f"{{{NS}}}tr")

        headers = [
            "Allergen",
            "Type",
            "Reaction",
            "Severity",
            "Status",
            "Onset Date",
        ]
        for header_text in headers:
            th = etree.SubElement(tr, f"{{{NS}}}th")
            th.text = header_text

        # Table body
        tbody = etree.SubElement(table, f"{{{NS}}}tbody")

        for idx, allergy in enumerate(self.allergies, start=1):
            tr = etree.SubElement(tbody, f"{{{NS}}}tr")

            # Allergen name (with ID reference)
            td_allergen = etree.SubElement(tr, f"{{{NS}}}td")
            content = etree.SubElement(
                td_allergen,
                f"{{{NS}}}content",
                ID=f"allergy-{idx}",
            )
            content.text = allergy.allergen

            # Type
            td_type = etree.SubElement(tr, f"{{{NS}}}td")
            td_type.text = allergy.allergy_type.capitalize()

            # Reaction
            td_reaction = etree.SubElement(tr, f"{{{NS}}}td")
            td_reaction.text = allergy.reaction if allergy.reaction else "Not specified"

            # Severity
            td_severity = etree.SubElement(tr, f"{{{NS}}}td")
            td_severity.text = (
                allergy.severity.capitalize() if allergy.severity else "Not specified"
            )

            # Status
            td_status = etree.SubElement(tr, f"{{{NS}}}td")
            td_status.text = allergy.status.capitalize()

            # Onset date
            td_onset = etree.SubElement(tr, f"{{{NS}}}td")
            if allergy.onset_date:
                td_onset.text = allergy.onset_date.strftime("%Y-%m-%d")
            else:
                td_onset.text = "Unknown"

    def _add_entry(self, section: etree._Element, allergy: AllergyProtocol) -> None:
        """
        Add entry element with Allergy Concern Act and Observation.

        Args:
            section: section element
            allergy: Allergy data
        """
        # Create entry element
        entry = etree.SubElement(section, f"{{{NS}}}entry", typeCode="DRIV")

        # Create Allergy Concern Act (wrapper for observations)
        act = etree.SubElement(
            entry,
            f"{{{NS}}}act",
            classCode="ACT",
            moodCode="EVN",
        )

        # Add template ID for Allergy Concern Act
        template_id = etree.SubElement(act, f"{{{NS}}}templateId")
        template_id.set("root", "2.16.840.1.113883.10.20.22.4.30")
        if self.version == CDAVersion.R2_1:
            template_id.set("extension", "2015-08-01")

        # Add ID
        import uuid

        id_elem = etree.SubElement(act, f"{{{NS}}}id")
        id_elem.set("root", "2.16.840.1.113883.19")
        id_elem.set("extension", str(uuid.uuid4()))

        # Add code
        code_elem = etree.SubElement(act, f"{{{NS}}}code")
        code_elem.set("code", "CONC")
        code_elem.set("codeSystem", "2.16.840.1.113883.5.6")
        code_elem.set("displayName", "Concern")

        # Add status code (active or completed based on allergy status)
        status = "active" if allergy.status.lower() == "active" else "completed"
        status_elem = StatusCode(status).to_element()
        act.append(status_elem)

        # Add effective time (low = onset date, high = resolved date if resolved)
        time_elem = etree.SubElement(act, f"{{{NS}}}effectiveTime")
        if allergy.onset_date:
            low_elem = etree.SubElement(time_elem, f"{{{NS}}}low")
            low_elem.set("value", allergy.onset_date.strftime("%Y%m%d"))
        if status == "completed":
            # If resolved, add high element (could use current date or specific resolved date)
            high_elem = etree.SubElement(time_elem, f"{{{NS}}}high")
            high_elem.set("nullFlavor", "UNK")

        # Add entryRelationship with Allergy Observation
        entry_rel = etree.SubElement(
            act,
            f"{{{NS}}}entryRelationship",
            typeCode="SUBJ",
        )

        # Create and add Allergy Observation
        allergy_builder = AllergyObservation(allergy, version=self.version)
        entry_rel.append(allergy_builder.to_element())

Functions

__init__(allergies, title='Allergies and Intolerances', version=CDAVersion.R2_1, **kwargs)

Initialize AllergiesSection builder.

Parameters:

Name Type Description Default
allergies Sequence[AllergyProtocol]

List of allergies satisfying AllergyProtocol

required
title str

Section title (default: "Allergies and Intolerances")

'Allergies and Intolerances'
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/allergies.py
def __init__(
    self,
    allergies: Sequence[AllergyProtocol],
    title: str = "Allergies and Intolerances",
    version: CDAVersion = CDAVersion.R2_1,
    **kwargs,
):
    """
    Initialize AllergiesSection builder.

    Args:
        allergies: List of allergies satisfying AllergyProtocol
        title: Section title (default: "Allergies and Intolerances")
        version: C-CDA version (R2.1 or R2.0)
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.allergies = allergies
    self.title = title

build()

Build Allergies and Intolerances Section XML element.

Returns:

Type Description
Element

lxml Element for section

Source code in ccdakit/builders/sections/allergies.py
def build(self) -> etree.Element:
    """
    Build Allergies and Intolerances Section XML element.

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add template IDs
    self.add_template_ids(section)

    # Add section code (48765-2 = Allergies and adverse reactions Document)
    code_elem = Code(
        code="48765-2",
        system="LOINC",
        display_name="Allergies and adverse reactions Document",
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # Add title
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (HTML table)
    self._add_narrative(section)

    # Add entries with Allergy Concern Acts
    for allergy in self.allergies:
        self._add_entry(section, allergy)

    return section

AnesthesiaSection

AnesthesiaSection

Bases: CDAElement

Builder for C-CDA Anesthesia Section.

Records the type of anesthesia (e.g., general, local, regional) and may state the actual agents used. This may be a subsection of the Procedure Description Section. The full details of anesthesia are usually found in a separate Anesthesia Note.

Includes narrative (HTML table) and structured entries with: - Procedure Activity Procedure (V2) - for anesthesia type/procedure - Medication Activity (V2) - for anesthesia agents/medications

Supports R2.1 version (2014-06-09).

Template ID: 2.16.840.1.113883.10.20.22.2.25 (2014-06-09) LOINC Code: 59774-0 (Anesthesia)

Source code in ccdakit/builders/sections/anesthesia.py
class AnesthesiaSection(CDAElement):
    """
    Builder for C-CDA Anesthesia Section.

    Records the type of anesthesia (e.g., general, local, regional) and may state
    the actual agents used. This may be a subsection of the Procedure Description
    Section. The full details of anesthesia are usually found in a separate
    Anesthesia Note.

    Includes narrative (HTML table) and structured entries with:
    - Procedure Activity Procedure (V2) - for anesthesia type/procedure
    - Medication Activity (V2) - for anesthesia agents/medications

    Supports R2.1 version (2014-06-09).

    Template ID: 2.16.840.1.113883.10.20.22.2.25 (2014-06-09)
    LOINC Code: 59774-0 (Anesthesia)
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.25",
                extension="2014-06-09",
                description="Anesthesia Section (V2) R2.1",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.25",
                extension="2014-06-09",
                description="Anesthesia Section (V2) R2.0",
            ),
        ],
    }

    def __init__(
        self,
        anesthesia_records: Sequence[AnesthesiaProtocol],
        title: str = "Anesthesia",
        version: CDAVersion = CDAVersion.R2_1,
        **kwargs,
    ):
        """
        Initialize AnesthesiaSection builder.

        Args:
            anesthesia_records: List of anesthesia data satisfying AnesthesiaProtocol
            title: Section title (default: "Anesthesia")
            version: C-CDA version (R2.1 or R2.0)
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.anesthesia_records = anesthesia_records
        self.title = title

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

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add template IDs
        self.add_template_ids(section)

        # Add section code (59774-0 = Anesthesia)
        code_elem = Code(
            code="59774-0",
            system="LOINC",
            display_name="Anesthesia",
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # Add title
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (HTML table)
        self._add_narrative(section)

        # Add entries with Anesthesia Procedures and Medications
        for anesthesia in self.anesthesia_records:
            # Add anesthesia procedure entry
            self._add_procedure_entry(section, anesthesia)

            # Add medication entries if anesthesia agents are provided
            if anesthesia.anesthesia_agents:
                for agent in anesthesia.anesthesia_agents:
                    self._add_medication_entry(section, agent)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element with HTML table.

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        if not self.anesthesia_records:
            # No anesthesia records - add "No anesthesia recorded" paragraph
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            paragraph.text = "No anesthesia recorded"
            return

        # Create table for anesthesia records
        table = etree.SubElement(text, f"{{{NS}}}table", border="1", width="100%")

        # Table header
        thead = etree.SubElement(table, f"{{{NS}}}thead")
        tr = etree.SubElement(thead, f"{{{NS}}}tr")

        headers = [
            "Anesthesia Type",
            "Code",
            "Status",
            "Start Time",
            "End Time",
            "Route",
            "Agents",
            "Performer",
        ]
        for header_text in headers:
            th = etree.SubElement(tr, f"{{{NS}}}th")
            th.text = header_text

        # Table body
        tbody = etree.SubElement(table, f"{{{NS}}}tbody")

        for idx, anesthesia in enumerate(self.anesthesia_records, start=1):
            tr = etree.SubElement(tbody, f"{{{NS}}}tr")

            # Anesthesia type (with ID reference)
            td_type = etree.SubElement(tr, f"{{{NS}}}td")
            content = etree.SubElement(
                td_type,
                f"{{{NS}}}content",
                ID=f"anesthesia-{idx}",
            )
            content.text = anesthesia.anesthesia_type

            # Code
            td_code = etree.SubElement(tr, f"{{{NS}}}td")
            td_code.text = f"{anesthesia.anesthesia_code} ({anesthesia.anesthesia_code_system})"

            # Status
            td_status = etree.SubElement(tr, f"{{{NS}}}td")
            td_status.text = anesthesia.status.capitalize()

            # Start time
            td_start = etree.SubElement(tr, f"{{{NS}}}td")
            if anesthesia.start_time:
                # Format based on whether it's a date or datetime
                if hasattr(anesthesia.start_time, "hour"):
                    # It's a datetime
                    td_start.text = anesthesia.start_time.strftime("%Y-%m-%d %H:%M")
                else:
                    # It's a date
                    td_start.text = anesthesia.start_time.strftime("%Y-%m-%d")
            else:
                td_start.text = "-"

            # End time
            td_end = etree.SubElement(tr, f"{{{NS}}}td")
            if anesthesia.end_time:
                # Format based on whether it's a date or datetime
                if hasattr(anesthesia.end_time, "hour"):
                    # It's a datetime
                    td_end.text = anesthesia.end_time.strftime("%Y-%m-%d %H:%M")
                else:
                    # It's a date
                    td_end.text = anesthesia.end_time.strftime("%Y-%m-%d")
            else:
                td_end.text = "-"

            # Route
            td_route = etree.SubElement(tr, f"{{{NS}}}td")
            if anesthesia.route:
                td_route.text = anesthesia.route
            else:
                td_route.text = "-"

            # Agents/medications
            td_agents = etree.SubElement(tr, f"{{{NS}}}td")
            if anesthesia.anesthesia_agents:
                agents_list = [agent.name for agent in anesthesia.anesthesia_agents]
                td_agents.text = ", ".join(agents_list)
            else:
                td_agents.text = "-"

            # Performer
            td_performer = etree.SubElement(tr, f"{{{NS}}}td")
            if anesthesia.performer_name:
                td_performer.text = anesthesia.performer_name
            else:
                td_performer.text = "-"

    def _add_procedure_entry(self, section: etree._Element, anesthesia: AnesthesiaProtocol) -> None:
        """
        Add entry element with Procedure Activity for anesthesia type.

        Args:
            section: section element
            anesthesia: Anesthesia data
        """
        # Create entry element
        entry = etree.SubElement(section, f"{{{NS}}}entry", typeCode="DRIV")

        # Create and add Anesthesia Procedure Activity
        proc_builder = AnesthesiaProcedure(anesthesia, version=self.version)
        entry.append(proc_builder.to_element())

    def _add_medication_entry(self, section: etree._Element, medication) -> None:
        """
        Add entry element with Medication Activity for anesthesia agent.

        Args:
            section: section element
            medication: Medication data (anesthesia agent)
        """
        # Create entry element
        entry = etree.SubElement(section, f"{{{NS}}}entry", typeCode="DRIV")

        # Create and add Medication Activity
        med_builder = MedicationActivity(medication, version=self.version)
        entry.append(med_builder.to_element())

Functions

__init__(anesthesia_records, title='Anesthesia', version=CDAVersion.R2_1, **kwargs)

Initialize AnesthesiaSection builder.

Parameters:

Name Type Description Default
anesthesia_records Sequence[AnesthesiaProtocol]

List of anesthesia data satisfying AnesthesiaProtocol

required
title str

Section title (default: "Anesthesia")

'Anesthesia'
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/anesthesia.py
def __init__(
    self,
    anesthesia_records: Sequence[AnesthesiaProtocol],
    title: str = "Anesthesia",
    version: CDAVersion = CDAVersion.R2_1,
    **kwargs,
):
    """
    Initialize AnesthesiaSection builder.

    Args:
        anesthesia_records: List of anesthesia data satisfying AnesthesiaProtocol
        title: Section title (default: "Anesthesia")
        version: C-CDA version (R2.1 or R2.0)
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.anesthesia_records = anesthesia_records
    self.title = title

build()

Build Anesthesia Section XML element.

Returns:

Type Description
Element

lxml Element for section

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

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add template IDs
    self.add_template_ids(section)

    # Add section code (59774-0 = Anesthesia)
    code_elem = Code(
        code="59774-0",
        system="LOINC",
        display_name="Anesthesia",
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # Add title
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (HTML table)
    self._add_narrative(section)

    # Add entries with Anesthesia Procedures and Medications
    for anesthesia in self.anesthesia_records:
        # Add anesthesia procedure entry
        self._add_procedure_entry(section, anesthesia)

        # Add medication entries if anesthesia agents are provided
        if anesthesia.anesthesia_agents:
            for agent in anesthesia.anesthesia_agents:
                self._add_medication_entry(section, agent)

    return section

AssessmentAndPlanSection

AssessmentAndPlanSection

Bases: CDAElement

Builder for C-CDA Assessment and Plan Section (V2).

This section represents the clinician's conclusions and working assumptions that will guide treatment of the patient. It may contain planned activities.

Template ID: 2.16.840.1.113883.10.20.22.2.9 Version: 2014-06-09

The Assessment and Plan Section may be combined or separated to meet local policy requirements. See also: - Assessment Section: 2.16.840.1.113883.10.20.22.2.8 - Plan of Treatment Section (V2): 2.16.840.1.113883.10.20.22.2.10

Source code in ccdakit/builders/sections/assessment_and_plan.py
class AssessmentAndPlanSection(CDAElement):
    """
    Builder for C-CDA Assessment and Plan Section (V2).

    This section represents the clinician's conclusions and working assumptions
    that will guide treatment of the patient. It may contain planned activities.

    Template ID: 2.16.840.1.113883.10.20.22.2.9
    Version: 2014-06-09

    The Assessment and Plan Section may be combined or separated to meet local
    policy requirements. See also:
    - Assessment Section: 2.16.840.1.113883.10.20.22.2.8
    - Plan of Treatment Section (V2): 2.16.840.1.113883.10.20.22.2.10
    """

    # Template IDs - Only one version (R2.0 and R2.1 use the same template)
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.9",
                extension="2014-06-09",
                description="Assessment and Plan Section (V2)",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.9",
                extension="2014-06-09",
                description="Assessment and Plan Section (V2)",
            ),
        ],
    }

    def __init__(
        self,
        items: Sequence[AssessmentAndPlanItemProtocol] = None,
        title: str = "Assessment and Plan",
        version: CDAVersion = CDAVersion.R2_1,
        **kwargs,
    ):
        """
        Initialize AssessmentAndPlanSection builder.

        Args:
            items: List of assessment/plan items satisfying AssessmentAndPlanItemProtocol.
                   Can be empty for narrative-only sections.
            title: Section title (default: "Assessment and Plan")
            version: C-CDA version (R2.1 or R2.0)
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.items = items if items is not None else []
        self.title = title

    def build(self) -> etree.Element:
        """
        Build Assessment and Plan Section XML element.

        Conformance:
            - CONF:1098-7705, 10381, 32583: SHALL contain templateId
            - CONF:1098-15353, 15354, 32141: SHALL contain code="51847-2"
            - CONF:1098-7707: SHALL contain text
            - CONF:1098-7708, 15448: MAY contain entry with Planned Act (V2)

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add template IDs (CONF:1098-7705, 10381, 32583)
        self.add_template_ids(section)

        # Add section code (CONF:1098-15353, 15354, 32141)
        code_elem = Code(
            code="51847-2",
            system="LOINC",
            display_name="Assessment and Plan",
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # Add title
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (CONF:1098-7707)
        self._add_narrative(section)

        # Add entries with Planned Acts (CONF:1098-7708, 15448)
        for item in self.items:
            if item.planned_act:
                self._add_entry(section, item.planned_act)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element with HTML content.

        The narrative can include both assessment findings and plan items.
        If no items are provided, generates a simple placeholder.

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        if not self.items:
            # No items - add generic paragraph
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            paragraph.text = "Assessment and plan documented in clinical notes."
            return

        # Separate items by type
        assessments = [item for item in self.items if item.item_type == "assessment"]
        plans = [item for item in self.items if item.item_type == "plan"]

        # Add assessment section if present
        if assessments:
            self._add_narrative_section(text, "Assessment", assessments)

        # Add plan section if present
        if plans:
            self._add_narrative_section(text, "Plan", plans)

    def _add_narrative_section(
        self,
        text: etree._Element,
        heading: str,
        items: Sequence[AssessmentAndPlanItemProtocol],
    ) -> None:
        """
        Add a subsection (Assessment or Plan) to the narrative.

        Args:
            text: Parent text element
            heading: Section heading ("Assessment" or "Plan")
            items: List of items to include
        """
        # Add heading
        paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
        content = etree.SubElement(paragraph, f"{{{NS}}}content", styleCode="Bold")
        content.text = heading

        # Add items as a list
        list_elem = etree.SubElement(text, f"{{{NS}}}list", listType="unordered")

        for idx, item in enumerate(items, start=1):
            li = etree.SubElement(list_elem, f"{{{NS}}}item")
            content = etree.SubElement(li, f"{{{NS}}}content", ID=f"{heading.lower()}-{idx}")
            content.text = item.text

    def _add_entry(
        self,
        section: etree._Element,
        planned_act: "PlannedAct",
    ) -> None:
        """
        Add entry element with Planned Act.

        Per spec CONF:1098-7708, 15448:
        - MAY contain zero or more [0..*] entry
        - SHALL contain exactly one [1..1] Planned Act (V2)

        Args:
            section: section element
            planned_act: Planned act data
        """
        # Create entry element
        entry = etree.SubElement(section, f"{{{NS}}}entry")

        # Create and add Planned Act
        act_builder = PlannedAct(planned_act, version=self.version)
        entry.append(act_builder.to_element())

Functions

__init__(items=None, title='Assessment and Plan', version=CDAVersion.R2_1, **kwargs)

Initialize AssessmentAndPlanSection builder.

Parameters:

Name Type Description Default
items Sequence[AssessmentAndPlanItemProtocol]

List of assessment/plan items satisfying AssessmentAndPlanItemProtocol. Can be empty for narrative-only sections.

None
title str

Section title (default: "Assessment and Plan")

'Assessment and Plan'
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/assessment_and_plan.py
def __init__(
    self,
    items: Sequence[AssessmentAndPlanItemProtocol] = None,
    title: str = "Assessment and Plan",
    version: CDAVersion = CDAVersion.R2_1,
    **kwargs,
):
    """
    Initialize AssessmentAndPlanSection builder.

    Args:
        items: List of assessment/plan items satisfying AssessmentAndPlanItemProtocol.
               Can be empty for narrative-only sections.
        title: Section title (default: "Assessment and Plan")
        version: C-CDA version (R2.1 or R2.0)
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.items = items if items is not None else []
    self.title = title

build()

Build Assessment and Plan Section XML element.

Conformance
  • CONF:1098-7705, 10381, 32583: SHALL contain templateId
  • CONF:1098-15353, 15354, 32141: SHALL contain code="51847-2"
  • CONF:1098-7707: SHALL contain text
  • CONF:1098-7708, 15448: MAY contain entry with Planned Act (V2)

Returns:

Type Description
Element

lxml Element for section

Source code in ccdakit/builders/sections/assessment_and_plan.py
def build(self) -> etree.Element:
    """
    Build Assessment and Plan Section XML element.

    Conformance:
        - CONF:1098-7705, 10381, 32583: SHALL contain templateId
        - CONF:1098-15353, 15354, 32141: SHALL contain code="51847-2"
        - CONF:1098-7707: SHALL contain text
        - CONF:1098-7708, 15448: MAY contain entry with Planned Act (V2)

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add template IDs (CONF:1098-7705, 10381, 32583)
    self.add_template_ids(section)

    # Add section code (CONF:1098-15353, 15354, 32141)
    code_elem = Code(
        code="51847-2",
        system="LOINC",
        display_name="Assessment and Plan",
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # Add title
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (CONF:1098-7707)
    self._add_narrative(section)

    # Add entries with Planned Acts (CONF:1098-7708, 15448)
    for item in self.items:
        if item.planned_act:
            self._add_entry(section, item.planned_act)

    return section

ChiefComplaintAndReasonForVisitSection

ChiefComplaintAndReasonForVisitSection

Bases: CDAElement

Builder for C-CDA Chief Complaint and Reason for Visit Section.

This section records the patient's chief complaint (the patient's own description) and/or the reason for the patient's visit (the provider's description of the reason for visit). Local policy determines whether the information is divided into two sections or recorded in one section serving both purposes.

Template: 2.16.840.1.113883.10.20.22.2.13 Code: 46239-0 (Chief Complaint and Reason for Visit) from LOINC

This section contains only narrative text - no structured entries are required. Supports both R2.1 and R2.0 versions.

Conformance: - CONF:81-7840: SHALL contain exactly one [1..1] templateId - CONF:81-10383: templateId/@root="2.16.840.1.113883.10.20.22.2.13" - CONF:81-15449: SHALL contain exactly one [1..1] code - CONF:81-15450: code/@code="46239-0" - CONF:81-26473: code/@codeSystem="2.16.840.1.113883.6.1" (LOINC) - CONF:81-7842: SHALL contain exactly one [1..1] title - CONF:81-7843: SHALL contain exactly one [1..1] text

Source code in ccdakit/builders/sections/chief_complaint_reason_for_visit.py
class ChiefComplaintAndReasonForVisitSection(CDAElement):
    """
    Builder for C-CDA Chief Complaint and Reason for Visit Section.

    This section records the patient's chief complaint (the patient's own description)
    and/or the reason for the patient's visit (the provider's description of the reason
    for visit). Local policy determines whether the information is divided into two
    sections or recorded in one section serving both purposes.

    Template: 2.16.840.1.113883.10.20.22.2.13
    Code: 46239-0 (Chief Complaint and Reason for Visit) from LOINC

    This section contains only narrative text - no structured entries are required.
    Supports both R2.1 and R2.0 versions.

    Conformance:
    - CONF:81-7840: SHALL contain exactly one [1..1] templateId
    - CONF:81-10383: templateId/@root="2.16.840.1.113883.10.20.22.2.13"
    - CONF:81-15449: SHALL contain exactly one [1..1] code
    - CONF:81-15450: code/@code="46239-0"
    - CONF:81-26473: code/@codeSystem="2.16.840.1.113883.6.1" (LOINC)
    - CONF:81-7842: SHALL contain exactly one [1..1] title
    - CONF:81-7843: SHALL contain exactly one [1..1] text
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.13",
                extension=None,  # No extension for this template
                description="Chief Complaint and Reason for Visit Section R2.1",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.13",
                extension=None,  # No extension for this template
                description="Chief Complaint and Reason for Visit Section R2.0",
            ),
        ],
    }

    # LOINC code for chief complaint and reason for visit section
    SECTION_CODE = "46239-0"
    SECTION_DISPLAY = "Chief Complaint and Reason for Visit"

    def __init__(
        self,
        chief_complaints: Optional[Sequence[ChiefComplaintProtocol]] = None,
        title: str = "Chief Complaint and Reason for Visit",
        version: CDAVersion = CDAVersion.R2_1,
        **kwargs,
    ):
        """
        Initialize ChiefComplaintAndReasonForVisitSection builder.

        Args:
            chief_complaints: List of chief complaint/reason for visit items.
                            If None or empty, displays "No chief complaint or reason for visit documented"
            title: Section title (default: "Chief Complaint and Reason for Visit")
            version: C-CDA version (R2.1 or R2.0)
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.chief_complaints = chief_complaints if chief_complaints is not None else []
        self.title = title

    def build(self) -> etree.Element:
        """
        Build Chief Complaint and Reason for Visit Section XML element.

        Conformance:
        - CONF:81-7840: SHALL contain templateId
        - CONF:81-10383: templateId/@root="2.16.840.1.113883.10.20.22.2.13"
        - CONF:81-15449: SHALL contain code
        - CONF:81-15450: code/@code="46239-0"
        - CONF:81-26473: code/@codeSystem="2.16.840.1.113883.6.1" (LOINC)
        - CONF:81-7842: SHALL contain title
        - CONF:81-7843: SHALL contain text

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add template IDs (CONF:81-7840, CONF:81-10383)
        self.add_template_ids(section)

        # Add section code (CONF:81-15449, CONF:81-15450, CONF:81-26473)
        # 46239-0 = Chief Complaint and Reason for Visit (LOINC)
        code_elem = Code(
            code=self.SECTION_CODE,
            system="LOINC",
            display_name=self.SECTION_DISPLAY,
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # Add title (CONF:81-7842)
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (CONF:81-7843)
        self._add_narrative(section)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element.

        The narrative provides human-readable content for the section.
        When no chief complaints are present, displays appropriate message.
        When chief complaints exist, they are displayed as a list or paragraph.

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        if not self.chief_complaints:
            # No chief complaint - add default paragraph
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            paragraph.text = "No chief complaint or reason for visit documented"
            return

        # If there's only one chief complaint, display as paragraph
        if len(self.chief_complaints) == 1:
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            content = etree.SubElement(
                paragraph,
                f"{{{NS}}}content",
                ID="chief-complaint-1",
            )
            content.text = self.chief_complaints[0].text
            return

        # If there are multiple chief complaints, display as a list
        list_elem = etree.SubElement(text, f"{{{NS}}}list")
        for idx, complaint in enumerate(self.chief_complaints, start=1):
            item = etree.SubElement(list_elem, f"{{{NS}}}item")
            content = etree.SubElement(
                item,
                f"{{{NS}}}content",
                ID=f"chief-complaint-{idx}",
            )
            content.text = complaint.text

Functions

__init__(chief_complaints=None, title='Chief Complaint and Reason for Visit', version=CDAVersion.R2_1, **kwargs)

Initialize ChiefComplaintAndReasonForVisitSection builder.

Parameters:

Name Type Description Default
chief_complaints Optional[Sequence[ChiefComplaintProtocol]]

List of chief complaint/reason for visit items. If None or empty, displays "No chief complaint or reason for visit documented"

None
title str

Section title (default: "Chief Complaint and Reason for Visit")

'Chief Complaint and Reason for Visit'
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/chief_complaint_reason_for_visit.py
def __init__(
    self,
    chief_complaints: Optional[Sequence[ChiefComplaintProtocol]] = None,
    title: str = "Chief Complaint and Reason for Visit",
    version: CDAVersion = CDAVersion.R2_1,
    **kwargs,
):
    """
    Initialize ChiefComplaintAndReasonForVisitSection builder.

    Args:
        chief_complaints: List of chief complaint/reason for visit items.
                        If None or empty, displays "No chief complaint or reason for visit documented"
        title: Section title (default: "Chief Complaint and Reason for Visit")
        version: C-CDA version (R2.1 or R2.0)
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.chief_complaints = chief_complaints if chief_complaints is not None else []
    self.title = title

build()

Build Chief Complaint and Reason for Visit Section XML element.

Conformance: - CONF:81-7840: SHALL contain templateId - CONF:81-10383: templateId/@root="2.16.840.1.113883.10.20.22.2.13" - CONF:81-15449: SHALL contain code - CONF:81-15450: code/@code="46239-0" - CONF:81-26473: code/@codeSystem="2.16.840.1.113883.6.1" (LOINC) - CONF:81-7842: SHALL contain title - CONF:81-7843: SHALL contain text

Returns:

Type Description
Element

lxml Element for section

Source code in ccdakit/builders/sections/chief_complaint_reason_for_visit.py
def build(self) -> etree.Element:
    """
    Build Chief Complaint and Reason for Visit Section XML element.

    Conformance:
    - CONF:81-7840: SHALL contain templateId
    - CONF:81-10383: templateId/@root="2.16.840.1.113883.10.20.22.2.13"
    - CONF:81-15449: SHALL contain code
    - CONF:81-15450: code/@code="46239-0"
    - CONF:81-26473: code/@codeSystem="2.16.840.1.113883.6.1" (LOINC)
    - CONF:81-7842: SHALL contain title
    - CONF:81-7843: SHALL contain text

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add template IDs (CONF:81-7840, CONF:81-10383)
    self.add_template_ids(section)

    # Add section code (CONF:81-15449, CONF:81-15450, CONF:81-26473)
    # 46239-0 = Chief Complaint and Reason for Visit (LOINC)
    code_elem = Code(
        code=self.SECTION_CODE,
        system="LOINC",
        display_name=self.SECTION_DISPLAY,
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # Add title (CONF:81-7842)
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (CONF:81-7843)
    self._add_narrative(section)

    return section

ComplicationsSection

ComplicationsSection

Bases: CDAElement

Builder for C-CDA Complications Section.

This section contains problems that occurred during or around the time of a procedure. The complications may be known risks or unanticipated problems.

Includes narrative (HTML table) and structured entries using Problem Observations. Supports R2.1 (2015-08-01) version.

Source code in ccdakit/builders/sections/complications.py
class ComplicationsSection(CDAElement):
    """
    Builder for C-CDA Complications Section.

    This section contains problems that occurred during or around the time of a procedure.
    The complications may be known risks or unanticipated problems.

    Includes narrative (HTML table) and structured entries using Problem Observations.
    Supports R2.1 (2015-08-01) version.
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.37",
                extension="2015-08-01",
                description="Complications Section (V3) R2.1",
            ),
        ],
    }

    def __init__(
        self,
        complications: Sequence[ComplicationProtocol],
        title: str = "Complications",
        version: CDAVersion = CDAVersion.R2_1,
        **kwargs,
    ):
        """
        Initialize ComplicationsSection builder.

        Args:
            complications: List of complications satisfying ComplicationProtocol
            title: Section title (default: "Complications")
            version: C-CDA version (R2.1)
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.complications = complications
        self.title = title

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

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add template IDs
        self.add_template_ids(section)

        # Add section code (55109-3 = Complications)
        code_elem = Code(
            code="55109-3",
            system="LOINC",
            display_name="Complications",
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # Add title
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (HTML table)
        self._add_narrative(section)

        # Add entries with Problem Observations
        for complication in self.complications:
            self._add_entry(section, complication)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element with HTML table.

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        if not self.complications:
            # No complications - add "No complications" paragraph
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            paragraph.text = "No complications"
            return

        # Create table for complications
        table = etree.SubElement(text, f"{{{NS}}}table", border="1", width="100%")

        # Table header
        thead = etree.SubElement(table, f"{{{NS}}}thead")
        tr = etree.SubElement(thead, f"{{{NS}}}tr")

        headers = ["Complication", "Code", "Severity", "Status", "Onset Date", "Resolved Date"]
        for header_text in headers:
            th = etree.SubElement(tr, f"{{{NS}}}th")
            th.text = header_text

        # Table body
        tbody = etree.SubElement(table, f"{{{NS}}}tbody")

        for idx, complication in enumerate(self.complications, start=1):
            tr = etree.SubElement(tbody, f"{{{NS}}}tr")

            # Complication name (with ID reference)
            td_name = etree.SubElement(tr, f"{{{NS}}}td")
            content = etree.SubElement(
                td_name,
                f"{{{NS}}}content",
                ID=f"complication-{idx}",
            )
            content.text = complication.name

            # Code
            td_code = etree.SubElement(tr, f"{{{NS}}}td")
            td_code.text = f"{complication.code} ({complication.code_system})"

            # Severity
            td_severity = etree.SubElement(tr, f"{{{NS}}}td")
            if complication.severity:
                td_severity.text = complication.severity.capitalize()
            else:
                td_severity.text = "Not specified"

            # Status
            td_status = etree.SubElement(tr, f"{{{NS}}}td")
            td_status.text = complication.status.capitalize()

            # Onset date
            td_onset = etree.SubElement(tr, f"{{{NS}}}td")
            if complication.onset_date:
                td_onset.text = complication.onset_date.strftime("%Y-%m-%d")
            else:
                td_onset.text = "Unknown"

            # Resolved date
            td_resolved = etree.SubElement(tr, f"{{{NS}}}td")
            if complication.resolved_date:
                td_resolved.text = complication.resolved_date.strftime("%Y-%m-%d")
            else:
                td_resolved.text = "Ongoing" if complication.status == "active" else "Unknown"

    def _add_entry(self, section: etree._Element, complication: ComplicationProtocol) -> None:
        """
        Add entry element with Problem Observation.

        According to the C-CDA specification, complications are represented using
        Problem Observation entries (2.16.840.1.113883.10.20.22.4.4).

        Args:
            section: section element
            complication: Complication data
        """
        # Create entry element
        entry = etree.SubElement(section, f"{{{NS}}}entry")

        # Create and add Problem Observation
        # Complications use Problem Observation directly, not wrapped in Concern Act
        obs_builder = ProblemObservation(complication, version=self.version)
        entry.append(obs_builder.to_element())

Functions

__init__(complications, title='Complications', version=CDAVersion.R2_1, **kwargs)

Initialize ComplicationsSection builder.

Parameters:

Name Type Description Default
complications Sequence[ComplicationProtocol]

List of complications satisfying ComplicationProtocol

required
title str

Section title (default: "Complications")

'Complications'
version CDAVersion

C-CDA version (R2.1)

R2_1
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/complications.py
def __init__(
    self,
    complications: Sequence[ComplicationProtocol],
    title: str = "Complications",
    version: CDAVersion = CDAVersion.R2_1,
    **kwargs,
):
    """
    Initialize ComplicationsSection builder.

    Args:
        complications: List of complications satisfying ComplicationProtocol
        title: Section title (default: "Complications")
        version: C-CDA version (R2.1)
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.complications = complications
    self.title = title

build()

Build Complications Section XML element.

Returns:

Type Description
Element

lxml Element for section

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

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add template IDs
    self.add_template_ids(section)

    # Add section code (55109-3 = Complications)
    code_elem = Code(
        code="55109-3",
        system="LOINC",
        display_name="Complications",
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # Add title
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (HTML table)
    self._add_narrative(section)

    # Add entries with Problem Observations
    for complication in self.complications:
        self._add_entry(section, complication)

    return section

DischargeDiagnosisSection

DischargeDiagnosisSection

Bases: CDAElement

Builder for C-CDA Discharge Diagnosis Section.

This template represents problems or diagnoses present at the time of discharge which occurred during the hospitalization. This section includes an optional entry to record patient diagnoses specific to this visit. Problems that need ongoing tracking should also be included in the Problem Section.

Template ID: 2.16.840.1.113883.10.20.22.2.24

Includes narrative (HTML table) and structured entries. Supports both R2.1 (2015-08-01) and R2.0 (2014-06-09) versions.

Source code in ccdakit/builders/sections/discharge_diagnosis.py
class DischargeDiagnosisSection(CDAElement):
    """
    Builder for C-CDA Discharge Diagnosis Section.

    This template represents problems or diagnoses present at the time of discharge
    which occurred during the hospitalization. This section includes an optional entry
    to record patient diagnoses specific to this visit. Problems that need ongoing
    tracking should also be included in the Problem Section.

    Template ID: 2.16.840.1.113883.10.20.22.2.24

    Includes narrative (HTML table) and structured entries.
    Supports both R2.1 (2015-08-01) and R2.0 (2014-06-09) versions.
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.24",
                extension="2015-08-01",
                description="Discharge Diagnosis Section (V3)",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.24",
                extension="2014-06-09",
                description="Discharge Diagnosis Section (V2)",
            ),
        ],
    }

    def __init__(
        self,
        diagnoses: Sequence[DischargeDiagnosisProtocol],
        title: str = "Discharge Diagnosis",
        version: CDAVersion = CDAVersion.R2_1,
        **kwargs,
    ):
        """
        Initialize DischargeDiagnosisSection builder.

        Args:
            diagnoses: List of discharge diagnoses satisfying DischargeDiagnosisProtocol
            title: Section title (default: "Discharge Diagnosis")
            version: C-CDA version (R2.1 or R2.0)
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.diagnoses = diagnoses
        self.title = title

    def build(self) -> etree.Element:
        """
        Build Discharge Diagnosis Section XML element.

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add template IDs (CONF:1198-7979, CONF:1198-10394, CONF:1198-32549)
        self.add_template_ids(section)

        # Add section code (CONF:1198-15355, CONF:1198-15356, CONF:1198-30861)
        code_elem = etree.SubElement(section, f"{{{NS}}}code")
        code_elem.set("code", "11535-2")
        code_elem.set("codeSystem", "2.16.840.1.113883.6.1")
        code_elem.set("codeSystemName", "LOINC")
        code_elem.set("displayName", "Hospital Discharge Diagnosis")

        # Add translation (CONF:1198-32834, CONF:1198-32835, CONF:1198-32836)
        translation = etree.SubElement(code_elem, f"{{{NS}}}translation")
        translation.set("code", "78375-3")
        translation.set("codeSystem", "2.16.840.1.113883.6.1")
        translation.set("codeSystemName", "LOINC")
        translation.set("displayName", "Discharge Diagnosis")

        # Add title (CONF:1198-7981)
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (HTML table) (CONF:1198-7982)
        self._add_narrative(section)

        # Add entries with Hospital Discharge Diagnosis (CONF:1198-7983, CONF:1198-15489)
        if self.diagnoses:
            entry = etree.SubElement(section, f"{{{NS}}}entry")
            discharge_diag = HospitalDischargeDiagnosis(
                self.diagnoses,
                version=self.version,
            )
            entry.append(discharge_diag.to_element())

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element with HTML table.

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        if not self.diagnoses:
            # No diagnoses - add "No discharge diagnoses" paragraph
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            paragraph.text = "No discharge diagnoses"
            return

        # Create table for diagnoses
        table = etree.SubElement(text, f"{{{NS}}}table", border="1", width="100%")

        # Table header
        thead = etree.SubElement(table, f"{{{NS}}}thead")
        tr = etree.SubElement(thead, f"{{{NS}}}tr")

        headers = ["Diagnosis", "Code", "Status", "Diagnosis Date"]
        for header_text in headers:
            th = etree.SubElement(tr, f"{{{NS}}}th")
            th.text = header_text

        # Table body
        tbody = etree.SubElement(table, f"{{{NS}}}tbody")

        for idx, diagnosis in enumerate(self.diagnoses, start=1):
            tr = etree.SubElement(tbody, f"{{{NS}}}tr")

            # Diagnosis name (with ID reference)
            td_name = etree.SubElement(tr, f"{{{NS}}}td")
            content = etree.SubElement(
                td_name,
                f"{{{NS}}}content",
                ID=f"discharge-diagnosis-{idx}",
            )
            content.text = diagnosis.name

            # Code
            td_code = etree.SubElement(tr, f"{{{NS}}}td")
            td_code.text = f"{diagnosis.code} ({diagnosis.code_system})"

            # Status
            td_status = etree.SubElement(tr, f"{{{NS}}}td")
            td_status.text = diagnosis.status.capitalize()

            # Diagnosis date
            td_date = etree.SubElement(tr, f"{{{NS}}}td")
            if diagnosis.diagnosis_date:
                td_date.text = diagnosis.diagnosis_date.strftime("%Y-%m-%d")
            else:
                td_date.text = "Unknown"

Functions

__init__(diagnoses, title='Discharge Diagnosis', version=CDAVersion.R2_1, **kwargs)

Initialize DischargeDiagnosisSection builder.

Parameters:

Name Type Description Default
diagnoses Sequence[DischargeDiagnosisProtocol]

List of discharge diagnoses satisfying DischargeDiagnosisProtocol

required
title str

Section title (default: "Discharge Diagnosis")

'Discharge Diagnosis'
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/discharge_diagnosis.py
def __init__(
    self,
    diagnoses: Sequence[DischargeDiagnosisProtocol],
    title: str = "Discharge Diagnosis",
    version: CDAVersion = CDAVersion.R2_1,
    **kwargs,
):
    """
    Initialize DischargeDiagnosisSection builder.

    Args:
        diagnoses: List of discharge diagnoses satisfying DischargeDiagnosisProtocol
        title: Section title (default: "Discharge Diagnosis")
        version: C-CDA version (R2.1 or R2.0)
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.diagnoses = diagnoses
    self.title = title

build()

Build Discharge Diagnosis Section XML element.

Returns:

Type Description
Element

lxml Element for section

Source code in ccdakit/builders/sections/discharge_diagnosis.py
def build(self) -> etree.Element:
    """
    Build Discharge Diagnosis Section XML element.

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add template IDs (CONF:1198-7979, CONF:1198-10394, CONF:1198-32549)
    self.add_template_ids(section)

    # Add section code (CONF:1198-15355, CONF:1198-15356, CONF:1198-30861)
    code_elem = etree.SubElement(section, f"{{{NS}}}code")
    code_elem.set("code", "11535-2")
    code_elem.set("codeSystem", "2.16.840.1.113883.6.1")
    code_elem.set("codeSystemName", "LOINC")
    code_elem.set("displayName", "Hospital Discharge Diagnosis")

    # Add translation (CONF:1198-32834, CONF:1198-32835, CONF:1198-32836)
    translation = etree.SubElement(code_elem, f"{{{NS}}}translation")
    translation.set("code", "78375-3")
    translation.set("codeSystem", "2.16.840.1.113883.6.1")
    translation.set("codeSystemName", "LOINC")
    translation.set("displayName", "Discharge Diagnosis")

    # Add title (CONF:1198-7981)
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (HTML table) (CONF:1198-7982)
    self._add_narrative(section)

    # Add entries with Hospital Discharge Diagnosis (CONF:1198-7983, CONF:1198-15489)
    if self.diagnoses:
        entry = etree.SubElement(section, f"{{{NS}}}entry")
        discharge_diag = HospitalDischargeDiagnosis(
            self.diagnoses,
            version=self.version,
        )
        entry.append(discharge_diag.to_element())

    return section

DischargeMedicationsSection

DischargeMedicationsSection

Bases: CDAElement

Builder for C-CDA Discharge Medications Section (entries required).

This section contains the medications the patient is intended to take or stop after discharge. Current, active medications must be listed. The section may also include a patient's prescription history and indicate the source of the medication list.

Conforms to: - Discharge Medications Section (entries required) (V3) template (2.16.840.1.113883.10.20.22.2.11.1:2015-08-01)

Includes narrative (HTML table) and structured entries. Supports both R2.1 (2015-08-01) and R2.0 (2015-08-01) versions.

Source code in ccdakit/builders/sections/discharge_medications.py
class DischargeMedicationsSection(CDAElement):
    """
    Builder for C-CDA Discharge Medications Section (entries required).

    This section contains the medications the patient is intended to take or stop after discharge.
    Current, active medications must be listed. The section may also include a patient's
    prescription history and indicate the source of the medication list.

    Conforms to:
    - Discharge Medications Section (entries required) (V3) template
      (2.16.840.1.113883.10.20.22.2.11.1:2015-08-01)

    Includes narrative (HTML table) and structured entries.
    Supports both R2.1 (2015-08-01) and R2.0 (2015-08-01) versions.
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.11.1",
                extension="2015-08-01",
                description="Discharge Medications Section (entries required) (V3) R2.1",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.11.1",
                extension="2015-08-01",
                description="Discharge Medications Section (entries required) (V3) R2.0",
            ),
        ],
    }

    # LOINC code system OID
    LOINC_OID = "2.16.840.1.113883.6.1"

    def __init__(
        self,
        medications: Sequence[MedicationProtocol],
        title: str = "Discharge Medications",
        version: CDAVersion = CDAVersion.R2_1,
        null_flavor: Optional[str] = None,
        **kwargs,
    ):
        """
        Initialize DischargeMedicationsSection builder.

        Args:
            medications: List of medications satisfying MedicationProtocol
            title: Section title (default: "Discharge Medications")
            version: C-CDA version (R2.1 or R2.0)
            null_flavor: Optional null flavor (e.g., "NI" for No Information)
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.medications = medications
        self.title = title
        self.null_flavor = null_flavor

    def build(self) -> etree.Element:
        """
        Build Discharge Medications Section XML element.

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add nullFlavor if specified (CONF:1198-32812)
        if self.null_flavor:
            section.set("nullFlavor", self.null_flavor)

        # Add template IDs (CONF:1198-7822, CONF:1198-10397, CONF:1198-32562)
        self.add_template_ids(section)

        # Add section code (CONF:1198-15361, CONF:1198-15362, CONF:1198-32145)
        self._add_code(section)

        # Add title (CONF:1198-7824)
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (CONF:1198-7825)
        self._add_narrative(section)

        # Add entries with Discharge Medication (CONF:1198-7826, CONF:1198-15491)
        # If section/@nullFlavor is not present: SHALL contain at least one entry
        if not self.null_flavor:
            for medication in self.medications:
                self._add_entry(section, medication)

        return section

    def _add_code(self, section: etree._Element) -> None:
        """
        Add code element to section.

        Args:
            section: section element
        """
        # CONF:1198-15361 - SHALL contain exactly one [1..1] code
        code_elem = etree.SubElement(section, f"{{{NS}}}code")

        # CONF:1198-15362 - code SHALL be "10183-2" Hospital Discharge Medications
        code_elem.set("code", "10183-2")

        # CONF:1198-32145 - codeSystem SHALL be LOINC
        code_elem.set("codeSystem", self.LOINC_OID)
        code_elem.set("codeSystemName", "LOINC")
        code_elem.set("displayName", "Hospital Discharge Medications")

        # CONF:1198-32857 - SHALL contain exactly one [1..1] translation
        translation = etree.SubElement(code_elem, f"{{{NS}}}translation")

        # CONF:1198-32858 - translation code SHALL be "75311-1" Discharge Medications
        translation.set("code", "75311-1")

        # CONF:1198-32859 - translation codeSystem SHALL be LOINC
        translation.set("codeSystem", self.LOINC_OID)
        translation.set("codeSystemName", "LOINC")
        translation.set("displayName", "Discharge Medications")

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element with HTML table.

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        if not self.medications:
            # No medications - add "No discharge medications" paragraph
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            if self.null_flavor:
                paragraph.text = "No information available for discharge medications"
            else:
                paragraph.text = "No discharge medications"
            return

        # Create table for medications
        table = etree.SubElement(text, f"{{{NS}}}table", border="1", width="100%")

        # Table header
        thead = etree.SubElement(table, f"{{{NS}}}thead")
        tr = etree.SubElement(thead, f"{{{NS}}}tr")

        headers = [
            "Medication",
            "Dosage",
            "Route",
            "Frequency",
            "Start Date",
            "End Date",
            "Status",
            "Instructions",
        ]
        for header_text in headers:
            th = etree.SubElement(tr, f"{{{NS}}}th")
            th.text = header_text

        # Table body
        tbody = etree.SubElement(table, f"{{{NS}}}tbody")

        for idx, medication in enumerate(self.medications, start=1):
            tr = etree.SubElement(tbody, f"{{{NS}}}tr")

            # Medication name (with ID reference)
            td_name = etree.SubElement(tr, f"{{{NS}}}td")
            content = etree.SubElement(
                td_name,
                f"{{{NS}}}content",
                ID=f"discharge-medication-{idx}",
            )
            content.text = medication.name

            # Dosage
            td_dosage = etree.SubElement(tr, f"{{{NS}}}td")
            td_dosage.text = medication.dosage

            # Route
            td_route = etree.SubElement(tr, f"{{{NS}}}td")
            td_route.text = medication.route.capitalize()

            # Frequency
            td_frequency = etree.SubElement(tr, f"{{{NS}}}td")
            td_frequency.text = medication.frequency

            # Start date
            td_start = etree.SubElement(tr, f"{{{NS}}}td")
            td_start.text = medication.start_date.strftime("%Y-%m-%d")

            # End date
            td_end = etree.SubElement(tr, f"{{{NS}}}td")
            if medication.end_date:
                td_end.text = medication.end_date.strftime("%Y-%m-%d")
            else:
                td_end.text = "Ongoing" if medication.status == "active" else "Unknown"

            # Status
            td_status = etree.SubElement(tr, f"{{{NS}}}td")
            td_status.text = medication.status.capitalize()

            # Instructions
            td_instructions = etree.SubElement(tr, f"{{{NS}}}td")
            if medication.instructions:
                td_instructions.text = medication.instructions
            else:
                td_instructions.text = "-"

    def _add_entry(self, section: etree._Element, medication: MedicationProtocol) -> None:
        """
        Add entry element with Discharge Medication.

        Args:
            section: section element
            medication: Medication data
        """
        # Create entry element
        entry = etree.SubElement(section, f"{{{NS}}}entry")

        # Create and add Discharge Medication
        discharge_med_builder = DischargeMedication(medication, version=self.version)
        entry.append(discharge_med_builder.to_element())

Functions

__init__(medications, title='Discharge Medications', version=CDAVersion.R2_1, null_flavor=None, **kwargs)

Initialize DischargeMedicationsSection builder.

Parameters:

Name Type Description Default
medications Sequence[MedicationProtocol]

List of medications satisfying MedicationProtocol

required
title str

Section title (default: "Discharge Medications")

'Discharge Medications'
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
null_flavor Optional[str]

Optional null flavor (e.g., "NI" for No Information)

None
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/discharge_medications.py
def __init__(
    self,
    medications: Sequence[MedicationProtocol],
    title: str = "Discharge Medications",
    version: CDAVersion = CDAVersion.R2_1,
    null_flavor: Optional[str] = None,
    **kwargs,
):
    """
    Initialize DischargeMedicationsSection builder.

    Args:
        medications: List of medications satisfying MedicationProtocol
        title: Section title (default: "Discharge Medications")
        version: C-CDA version (R2.1 or R2.0)
        null_flavor: Optional null flavor (e.g., "NI" for No Information)
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.medications = medications
    self.title = title
    self.null_flavor = null_flavor

build()

Build Discharge Medications Section XML element.

Returns:

Type Description
Element

lxml Element for section

Source code in ccdakit/builders/sections/discharge_medications.py
def build(self) -> etree.Element:
    """
    Build Discharge Medications Section XML element.

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add nullFlavor if specified (CONF:1198-32812)
    if self.null_flavor:
        section.set("nullFlavor", self.null_flavor)

    # Add template IDs (CONF:1198-7822, CONF:1198-10397, CONF:1198-32562)
    self.add_template_ids(section)

    # Add section code (CONF:1198-15361, CONF:1198-15362, CONF:1198-32145)
    self._add_code(section)

    # Add title (CONF:1198-7824)
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (CONF:1198-7825)
    self._add_narrative(section)

    # Add entries with Discharge Medication (CONF:1198-7826, CONF:1198-15491)
    # If section/@nullFlavor is not present: SHALL contain at least one entry
    if not self.null_flavor:
        for medication in self.medications:
            self._add_entry(section, medication)

    return section

EncountersSection

EncountersSection

Bases: CDAElement

Builder for C-CDA Encounters Section (entries required).

Includes narrative (HTML table) and structured entries. Supports both R2.1 (2015-08-01) and R2.0 (2014-06-09) versions.

Source code in ccdakit/builders/sections/encounters.py
class EncountersSection(CDAElement):
    """
    Builder for C-CDA Encounters Section (entries required).

    Includes narrative (HTML table) and structured entries.
    Supports both R2.1 (2015-08-01) and R2.0 (2014-06-09) versions.
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.22.1",
                extension="2015-08-01",
                description="Encounters Section (entries required) V3",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.22.1",
                extension="2014-06-09",
                description="Encounters Section (entries required) V2",
            ),
        ],
    }

    def __init__(
        self,
        encounters: Sequence[EncounterProtocol],
        title: str = "Encounters",
        version: CDAVersion = CDAVersion.R2_1,
        **kwargs,
    ):
        """
        Initialize EncountersSection builder.

        Args:
            encounters: List of encounters satisfying EncounterProtocol
            title: Section title (default: "Encounters")
            version: C-CDA version (R2.1 or R2.0)
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.encounters = encounters
        self.title = title

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

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add template IDs
        self.add_template_ids(section)

        # Add section code (46240-8 = Encounters)
        code_elem = Code(
            code="46240-8",
            system="LOINC",
            display_name="Encounters",
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # Add title
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (HTML table)
        self._add_narrative(section)

        # Add entries with Encounter Activities
        for encounter in self.encounters:
            self._add_entry(section, encounter)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element with HTML table.

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        if not self.encounters:
            # No encounters - add "No encounters recorded" paragraph
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            paragraph.text = "No encounters recorded"
            return

        # Create table for encounters
        table = etree.SubElement(text, f"{{{NS}}}table", border="1", width="100%")

        # Table header
        thead = etree.SubElement(table, f"{{{NS}}}thead")
        tr = etree.SubElement(thead, f"{{{NS}}}tr")

        headers = [
            "Encounter Type",
            "Code",
            "Date",
            "Location",
            "Performer",
            "Discharge Disposition",
        ]
        for header_text in headers:
            th = etree.SubElement(tr, f"{{{NS}}}th")
            th.text = header_text

        # Table body
        tbody = etree.SubElement(table, f"{{{NS}}}tbody")

        for idx, encounter in enumerate(self.encounters, start=1):
            tr = etree.SubElement(tbody, f"{{{NS}}}tr")

            # Encounter type (with ID reference)
            td_type = etree.SubElement(tr, f"{{{NS}}}td")
            content = etree.SubElement(
                td_type,
                f"{{{NS}}}content",
                ID=f"encounter-{idx}",
            )
            content.text = encounter.encounter_type

            # Code
            td_code = etree.SubElement(tr, f"{{{NS}}}td")
            td_code.text = f"{encounter.code} ({encounter.code_system})"

            # Date
            td_date = etree.SubElement(tr, f"{{{NS}}}td")
            if encounter.date:
                # Format based on whether it's a date or datetime
                if hasattr(encounter.date, "hour"):
                    # It's a datetime
                    date_str = encounter.date.strftime("%Y-%m-%d %H:%M")
                else:
                    # It's a date
                    date_str = encounter.date.strftime("%Y-%m-%d")

                # Add end date if available
                if encounter.end_date:
                    if hasattr(encounter.end_date, "hour"):
                        end_str = encounter.end_date.strftime("%Y-%m-%d %H:%M")
                    else:
                        end_str = encounter.end_date.strftime("%Y-%m-%d")
                    td_date.text = f"{date_str} to {end_str}"
                else:
                    td_date.text = date_str
            else:
                td_date.text = "Unknown"

            # Location
            td_location = etree.SubElement(tr, f"{{{NS}}}td")
            if encounter.location:
                td_location.text = encounter.location
            else:
                td_location.text = "-"

            # Performer
            td_performer = etree.SubElement(tr, f"{{{NS}}}td")
            if encounter.performer_name:
                td_performer.text = encounter.performer_name
            else:
                td_performer.text = "-"

            # Discharge Disposition
            td_disposition = etree.SubElement(tr, f"{{{NS}}}td")
            if encounter.discharge_disposition:
                td_disposition.text = encounter.discharge_disposition
            else:
                td_disposition.text = "-"

    def _add_entry(self, section: etree._Element, encounter: EncounterProtocol) -> None:
        """
        Add entry element with Encounter Activity.

        Args:
            section: section element
            encounter: Encounter data
        """
        # Create entry element
        entry = etree.SubElement(section, f"{{{NS}}}entry", typeCode="DRIV")

        # Create and add Encounter Activity
        enc_builder = EncounterActivity(encounter, version=self.version)
        entry.append(enc_builder.to_element())

Functions

__init__(encounters, title='Encounters', version=CDAVersion.R2_1, **kwargs)

Initialize EncountersSection builder.

Parameters:

Name Type Description Default
encounters Sequence[EncounterProtocol]

List of encounters satisfying EncounterProtocol

required
title str

Section title (default: "Encounters")

'Encounters'
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/encounters.py
def __init__(
    self,
    encounters: Sequence[EncounterProtocol],
    title: str = "Encounters",
    version: CDAVersion = CDAVersion.R2_1,
    **kwargs,
):
    """
    Initialize EncountersSection builder.

    Args:
        encounters: List of encounters satisfying EncounterProtocol
        title: Section title (default: "Encounters")
        version: C-CDA version (R2.1 or R2.0)
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.encounters = encounters
    self.title = title

build()

Build Encounters Section XML element.

Returns:

Type Description
Element

lxml Element for section

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

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add template IDs
    self.add_template_ids(section)

    # Add section code (46240-8 = Encounters)
    code_elem = Code(
        code="46240-8",
        system="LOINC",
        display_name="Encounters",
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # Add title
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (HTML table)
    self._add_narrative(section)

    # Add entries with Encounter Activities
    for encounter in self.encounters:
        self._add_entry(section, encounter)

    return section

FamilyHistorySection

FamilyHistorySection

Bases: CDAElement

Builder for C-CDA Family History Section.

This section contains data defining the patient's genetic relatives in terms of possible or relevant health risk factors that have a potential impact on the patient's healthcare risk profile.

Includes narrative (HTML table) and structured entries. Supports both R2.1 (2015-08-01) and R2.0 (2015-08-01) versions.

Conformance: 2.16.840.1.113883.10.20.22.2.15 (Family History Section V3)

Source code in ccdakit/builders/sections/family_history.py
class FamilyHistorySection(CDAElement):
    """
    Builder for C-CDA Family History Section.

    This section contains data defining the patient's genetic relatives in terms
    of possible or relevant health risk factors that have a potential impact on
    the patient's healthcare risk profile.

    Includes narrative (HTML table) and structured entries.
    Supports both R2.1 (2015-08-01) and R2.0 (2015-08-01) versions.

    Conformance: 2.16.840.1.113883.10.20.22.2.15 (Family History Section V3)
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.15",
                extension="2015-08-01",
                description="Family History Section (V3) R2.1",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.15",
                extension="2015-08-01",
                description="Family History Section (V3) R2.0",
            ),
        ],
    }

    def __init__(
        self,
        family_members: Sequence[FamilyMemberHistoryProtocol],
        title: str = "Family History",
        version: CDAVersion = CDAVersion.R2_1,
        **kwargs,
    ):
        """
        Initialize FamilyHistorySection builder.

        Args:
            family_members: List of family members satisfying FamilyMemberHistoryProtocol
            title: Section title (default: "Family History")
            version: C-CDA version (R2.1 or R2.0)
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.family_members = family_members
        self.title = title

    def build(self) -> etree.Element:
        """
        Build Family History Section XML element.

        Returns:
            lxml Element for section

        Conformance Rules:
            - CONF:1198-7932: SHALL contain templateId
            - CONF:1198-10388: templateId/@root="2.16.840.1.113883.10.20.22.2.15"
            - CONF:1198-32607: templateId/@extension="2015-08-01"
            - CONF:1198-15469: SHALL contain code
            - CONF:1198-15470: code/@code="10157-6"
            - CONF:1198-32481: code/@codeSystem="2.16.840.1.113883.6.1" (LOINC)
            - CONF:1198-7934: SHALL contain title
            - CONF:1198-7935: SHALL contain text
            - CONF:1198-32430: MAY contain entry
            - CONF:1198-32431: entry SHALL contain Family History Organizer
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add template IDs (CONF:1198-7932, CONF:1198-10388, CONF:1198-32607)
        self.add_template_ids(section)

        # Add section code (CONF:1198-15469, CONF:1198-15470, CONF:1198-32481)
        code_elem = Code(
            code="10157-6",
            system="LOINC",
            display_name="Family History",
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # Add title (CONF:1198-7934)
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (HTML table) (CONF:1198-7935)
        self._add_narrative(section)

        # Add entries with Family History Organizers (CONF:1198-32430, CONF:1198-32431)
        for family_member in self.family_members:
            self._add_entry(section, family_member)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element with HTML table.

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        if not self.family_members:
            # No family history - add "No known family history" paragraph
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            paragraph.text = "No known family history"
            return

        # Create table for family history
        table = etree.SubElement(text, f"{{{NS}}}table", border="1", width="100%")

        # Table header
        thead = etree.SubElement(table, f"{{{NS}}}thead")
        tr = etree.SubElement(thead, f"{{{NS}}}tr")

        headers = [
            "Family Member",
            "Gender",
            "Relationship",
            "Condition",
            "Age at Onset",
            "Status",
        ]
        for header_text in headers:
            th = etree.SubElement(tr, f"{{{NS}}}th")
            th.text = header_text

        # Table body
        tbody = etree.SubElement(table, f"{{{NS}}}tbody")

        for idx, family_member in enumerate(self.family_members, start=1):
            # Each family member can have multiple observations
            observations = family_member.observations
            if not observations:
                # Add row for family member with no observations
                self._add_family_member_row(tbody, idx, family_member, None, first_row=True)
            else:
                # Add row for each observation
                for obs_idx, observation in enumerate(observations):
                    self._add_family_member_row(
                        tbody,
                        idx,
                        family_member,
                        observation,
                        first_row=(obs_idx == 0),
                    )

    def _add_family_member_row(
        self,
        tbody: etree._Element,
        member_idx: int,
        family_member: FamilyMemberHistoryProtocol,
        observation,
        first_row: bool,
    ) -> None:
        """
        Add a table row for a family member observation.

        Args:
            tbody: Table body element
            member_idx: Family member index
            family_member: Family member data
            observation: Observation data (or None)
            first_row: Whether this is the first row for this family member
        """
        tr = etree.SubElement(tbody, f"{{{NS}}}tr")

        # Family member name/ID (with ID reference, only on first row)
        td_member = etree.SubElement(tr, f"{{{NS}}}td")
        if first_row:
            content = etree.SubElement(
                td_member,
                f"{{{NS}}}content",
                ID=f"family-member-{member_idx}",
            )
            content.text = f"Family Member {member_idx}"
        else:
            # Empty cell or just text for continuation rows
            td_member.text = ""

        # Gender (only on first row)
        td_gender = etree.SubElement(tr, f"{{{NS}}}td")
        if first_row and family_member.subject and family_member.subject.administrative_gender_code:
            gender_map = {
                "M": "Male",
                "F": "Female",
                "UN": "Undifferentiated",
            }
            td_gender.text = gender_map.get(
                family_member.subject.administrative_gender_code, "Unknown"
            )
        elif first_row:
            td_gender.text = "Unknown"
        else:
            td_gender.text = ""

        # Relationship (only on first row)
        td_relationship = etree.SubElement(tr, f"{{{NS}}}td")
        if first_row:
            td_relationship.text = family_member.relationship_display_name
        else:
            td_relationship.text = ""

        # Condition
        td_condition = etree.SubElement(tr, f"{{{NS}}}td")
        if observation:
            td_condition.text = observation.condition_name
        else:
            td_condition.text = "No conditions documented"

        # Age at onset
        td_age = etree.SubElement(tr, f"{{{NS}}}td")
        if observation and observation.age_at_onset is not None:
            td_age.text = f"{observation.age_at_onset} years"
        elif observation:
            td_age.text = "Unknown"
        else:
            td_age.text = ""

        # Status (deceased or living)
        td_status = etree.SubElement(tr, f"{{{NS}}}td")
        if first_row:
            if family_member.subject and family_member.subject.deceased_ind:
                if family_member.subject.deceased_time:
                    td_status.text = (
                        f"Deceased ({family_member.subject.deceased_time.strftime('%Y-%m-%d')})"
                    )
                else:
                    td_status.text = "Deceased"
            else:
                td_status.text = "Living"
        else:
            td_status.text = ""

    def _add_entry(
        self, section: etree._Element, family_member: FamilyMemberHistoryProtocol
    ) -> None:
        """
        Add entry element with Family History Organizer.

        Args:
            section: section element
            family_member: Family member data

        Conformance: CONF:1198-32430, CONF:1198-32431
        """
        # Create entry element
        entry = etree.SubElement(section, f"{{{NS}}}entry")

        # Create and add Family History Organizer (CONF:1198-32431)
        organizer_builder = FamilyHistoryOrganizer(family_member, version=self.version)
        entry.append(organizer_builder.to_element())

Functions

__init__(family_members, title='Family History', version=CDAVersion.R2_1, **kwargs)

Initialize FamilyHistorySection builder.

Parameters:

Name Type Description Default
family_members Sequence[FamilyMemberHistoryProtocol]

List of family members satisfying FamilyMemberHistoryProtocol

required
title str

Section title (default: "Family History")

'Family History'
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/family_history.py
def __init__(
    self,
    family_members: Sequence[FamilyMemberHistoryProtocol],
    title: str = "Family History",
    version: CDAVersion = CDAVersion.R2_1,
    **kwargs,
):
    """
    Initialize FamilyHistorySection builder.

    Args:
        family_members: List of family members satisfying FamilyMemberHistoryProtocol
        title: Section title (default: "Family History")
        version: C-CDA version (R2.1 or R2.0)
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.family_members = family_members
    self.title = title

build()

Build Family History Section XML element.

Returns:

Type Description
Element

lxml Element for section

Conformance Rules
  • CONF:1198-7932: SHALL contain templateId
  • CONF:1198-10388: templateId/@root="2.16.840.1.113883.10.20.22.2.15"
  • CONF:1198-32607: templateId/@extension="2015-08-01"
  • CONF:1198-15469: SHALL contain code
  • CONF:1198-15470: code/@code="10157-6"
  • CONF:1198-32481: code/@codeSystem="2.16.840.1.113883.6.1" (LOINC)
  • CONF:1198-7934: SHALL contain title
  • CONF:1198-7935: SHALL contain text
  • CONF:1198-32430: MAY contain entry
  • CONF:1198-32431: entry SHALL contain Family History Organizer
Source code in ccdakit/builders/sections/family_history.py
def build(self) -> etree.Element:
    """
    Build Family History Section XML element.

    Returns:
        lxml Element for section

    Conformance Rules:
        - CONF:1198-7932: SHALL contain templateId
        - CONF:1198-10388: templateId/@root="2.16.840.1.113883.10.20.22.2.15"
        - CONF:1198-32607: templateId/@extension="2015-08-01"
        - CONF:1198-15469: SHALL contain code
        - CONF:1198-15470: code/@code="10157-6"
        - CONF:1198-32481: code/@codeSystem="2.16.840.1.113883.6.1" (LOINC)
        - CONF:1198-7934: SHALL contain title
        - CONF:1198-7935: SHALL contain text
        - CONF:1198-32430: MAY contain entry
        - CONF:1198-32431: entry SHALL contain Family History Organizer
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add template IDs (CONF:1198-7932, CONF:1198-10388, CONF:1198-32607)
    self.add_template_ids(section)

    # Add section code (CONF:1198-15469, CONF:1198-15470, CONF:1198-32481)
    code_elem = Code(
        code="10157-6",
        system="LOINC",
        display_name="Family History",
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # Add title (CONF:1198-7934)
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (HTML table) (CONF:1198-7935)
    self._add_narrative(section)

    # Add entries with Family History Organizers (CONF:1198-32430, CONF:1198-32431)
    for family_member in self.family_members:
        self._add_entry(section, family_member)

    return section

FunctionalStatusSection

FunctionalStatusSection

Bases: CDAElement

Builder for C-CDA Functional Status Section.

Contains observations and assessments of a patient's physical abilities, including Activities of Daily Living (ADLs), Instrumental Activities of Daily Living (IADLs), mobility, self-care, and problems that impact function.

Includes narrative (HTML table) and structured entries. Supports both R2.1 (2014-06-09) and R2.0 (2014-06-09) versions.

Conformance: Template 2.16.840.1.113883.10.20.22.2.14

Source code in ccdakit/builders/sections/functional_status.py
class FunctionalStatusSection(CDAElement):
    """
    Builder for C-CDA Functional Status Section.

    Contains observations and assessments of a patient's physical abilities,
    including Activities of Daily Living (ADLs), Instrumental Activities of
    Daily Living (IADLs), mobility, self-care, and problems that impact function.

    Includes narrative (HTML table) and structured entries.
    Supports both R2.1 (2014-06-09) and R2.0 (2014-06-09) versions.

    Conformance: Template 2.16.840.1.113883.10.20.22.2.14
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.14",
                extension="2014-06-09",
                description="Functional Status Section R2.1",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.14",
                extension="2014-06-09",
                description="Functional Status Section R2.0",
            ),
        ],
    }

    # LOINC code for section per CONF:1098-14578, CONF:1098-14579
    SECTION_CODE = "47420-5"  # Functional Status

    def __init__(
        self,
        organizers: Sequence[FunctionalStatusOrganizerProtocol],
        title: str = "Functional Status",
        version: CDAVersion = CDAVersion.R2_1,
        **kwargs,
    ):
        """
        Initialize FunctionalStatusSection builder.

        Args:
            organizers: List of functional status organizers
            title: Section title (default: "Functional Status")
            version: C-CDA version (R2.1 or R2.0)
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.organizers = organizers
        self.title = title

    def build(self) -> etree.Element:
        """
        Build Functional Status Section XML element.

        Implements conformance rules:
        - CONF:1098-7920, CONF:1098-10389, CONF:1098-32567: templateId
        - CONF:1098-14578, CONF:1098-14579, CONF:1098-30866: code
        - CONF:1098-7922: title
        - CONF:1098-7923: text (narrative)
        - CONF:1098-14414, CONF:1098-14415: entry with organizer (optional)

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # CONF:1098-7920, CONF:1098-10389, CONF:1098-32567: Add template IDs
        self.add_template_ids(section)

        # CONF:1098-14578, CONF:1098-14579, CONF:1098-30866: Add section code
        code_elem = Code(
            code=self.SECTION_CODE,
            system="LOINC",
            display_name="Functional Status",
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # CONF:1098-7922: Add title
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # CONF:1098-7923: Add narrative text (HTML table)
        self._add_narrative(section)

        # CONF:1098-14414, CONF:1098-14415: Add entries with organizers
        for organizer in self.organizers:
            self._add_entry(section, organizer)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element with HTML table.

        CONF:1098-7923: SHALL contain exactly one [1..1] text

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        if not self.organizers:
            # No functional status data - add "No functional status recorded" paragraph
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            paragraph.text = "No functional status recorded"
            return

        # Create table for functional status
        table = etree.SubElement(text, f"{{{NS}}}table", border="1", width="100%")

        # Table header
        thead = etree.SubElement(table, f"{{{NS}}}thead")
        tr = etree.SubElement(thead, f"{{{NS}}}tr")

        headers = [
            "Category",
            "Functional Status",
            "Value",
            "Date/Time",
        ]
        for header_text in headers:
            th = etree.SubElement(tr, f"{{{NS}}}th")
            th.text = header_text

        # Table body
        tbody = etree.SubElement(table, f"{{{NS}}}tbody")

        for org_idx, organizer in enumerate(self.organizers, start=1):
            for obs_idx, observation in enumerate(organizer.observations, start=1):
                tr = etree.SubElement(tbody, f"{{{NS}}}tr")

                # Category
                td_category = etree.SubElement(tr, f"{{{NS}}}td")
                td_category.text = organizer.category

                # Functional Status type (with ID reference)
                td_type = etree.SubElement(tr, f"{{{NS}}}td")
                content = etree.SubElement(
                    td_type,
                    f"{{{NS}}}content",
                    ID=f"funcstatus-{org_idx}-{obs_idx}",
                )
                content.text = observation.type

                # Value
                td_value = etree.SubElement(tr, f"{{{NS}}}td")
                td_value.text = observation.value

                # Date/Time
                td_date = etree.SubElement(tr, f"{{{NS}}}td")
                td_date.text = observation.date.strftime("%Y-%m-%d %H:%M")

    def _add_entry(
        self, section: etree._Element, organizer: FunctionalStatusOrganizerProtocol
    ) -> None:
        """
        Add entry element with Functional Status Organizer.

        CONF:1098-14414: MAY contain zero or more [0..*] entry
        CONF:1098-14415: SHALL contain Functional Status Organizer

        Args:
            section: section element
            organizer: Functional status organizer data
        """
        # Create entry element
        entry = etree.SubElement(section, f"{{{NS}}}entry")

        # Create and add Functional Status Organizer
        organizer_builder = FunctionalStatusOrganizer(organizer, version=self.version)
        entry.append(organizer_builder.to_element())

Functions

__init__(organizers, title='Functional Status', version=CDAVersion.R2_1, **kwargs)

Initialize FunctionalStatusSection builder.

Parameters:

Name Type Description Default
organizers Sequence[FunctionalStatusOrganizerProtocol]

List of functional status organizers

required
title str

Section title (default: "Functional Status")

'Functional Status'
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/functional_status.py
def __init__(
    self,
    organizers: Sequence[FunctionalStatusOrganizerProtocol],
    title: str = "Functional Status",
    version: CDAVersion = CDAVersion.R2_1,
    **kwargs,
):
    """
    Initialize FunctionalStatusSection builder.

    Args:
        organizers: List of functional status organizers
        title: Section title (default: "Functional Status")
        version: C-CDA version (R2.1 or R2.0)
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.organizers = organizers
    self.title = title

build()

Build Functional Status Section XML element.

Implements conformance rules: - CONF:1098-7920, CONF:1098-10389, CONF:1098-32567: templateId - CONF:1098-14578, CONF:1098-14579, CONF:1098-30866: code - CONF:1098-7922: title - CONF:1098-7923: text (narrative) - CONF:1098-14414, CONF:1098-14415: entry with organizer (optional)

Returns:

Type Description
Element

lxml Element for section

Source code in ccdakit/builders/sections/functional_status.py
def build(self) -> etree.Element:
    """
    Build Functional Status Section XML element.

    Implements conformance rules:
    - CONF:1098-7920, CONF:1098-10389, CONF:1098-32567: templateId
    - CONF:1098-14578, CONF:1098-14579, CONF:1098-30866: code
    - CONF:1098-7922: title
    - CONF:1098-7923: text (narrative)
    - CONF:1098-14414, CONF:1098-14415: entry with organizer (optional)

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # CONF:1098-7920, CONF:1098-10389, CONF:1098-32567: Add template IDs
    self.add_template_ids(section)

    # CONF:1098-14578, CONF:1098-14579, CONF:1098-30866: Add section code
    code_elem = Code(
        code=self.SECTION_CODE,
        system="LOINC",
        display_name="Functional Status",
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # CONF:1098-7922: Add title
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # CONF:1098-7923: Add narrative text (HTML table)
    self._add_narrative(section)

    # CONF:1098-14414, CONF:1098-14415: Add entries with organizers
    for organizer in self.organizers:
        self._add_entry(section, organizer)

    return section

GoalsSection

GoalsSection

Bases: CDAElement

Builder for C-CDA Goals Section.

Includes narrative (HTML table) and structured entries. Supports both R2.1 and R2.0 versions.

Template ID: 2.16.840.1.113883.10.20.22.2.60

Source code in ccdakit/builders/sections/goals.py
class GoalsSection(CDAElement):
    """
    Builder for C-CDA Goals Section.

    Includes narrative (HTML table) and structured entries.
    Supports both R2.1 and R2.0 versions.

    Template ID: 2.16.840.1.113883.10.20.22.2.60
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.60",
                extension=None,  # No extension specified in spec
                description="Goals Section",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.60",
                extension=None,  # Same for R2.0
                description="Goals Section",
            ),
        ],
    }

    def __init__(
        self,
        goals: Sequence[GoalProtocol],
        title: str = "Goals",
        version: CDAVersion = CDAVersion.R2_1,
        null_flavor: str = None,
        **kwargs,
    ):
        """
        Initialize GoalsSection builder.

        Args:
            goals: List of goals satisfying GoalProtocol
            title: Section title (default: "Goals")
            version: C-CDA version (R2.1 or R2.0)
            null_flavor: Optional null flavor (e.g., "NI" for no information)
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.goals = goals
        self.title = title
        self.null_flavor = null_flavor

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

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add null flavor if specified (CONF:1098-32819)
        if self.null_flavor:
            section.set("nullFlavor", self.null_flavor)

        # Add template IDs (CONF:1098-29584, CONF:1098-29585)
        self.add_template_ids(section)

        # Add section code (CONF:1098-29586, CONF:1098-29587, CONF:1098-29588)
        # 61146-7 = Goals (LOINC)
        code_elem = Code(
            code="61146-7",
            system="LOINC",
            display_name="Goals",
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # Add title (CONF:1098-30721)
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (CONF:1098-30722)
        self._add_narrative(section)

        # Add entries with Goal Observations (CONF:1098-30719, CONF:1098-30720)
        if self.goals:
            for goal in self.goals:
                self._add_entry(section, goal)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element with HTML table.

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        if not self.goals:
            # No goals - add "No goals documented" paragraph
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            paragraph.text = "No goals documented"
            return

        # Create table for goals
        table = etree.SubElement(text, f"{{{NS}}}table", border="1", width="100%")

        # Table header
        thead = etree.SubElement(table, f"{{{NS}}}thead")
        tr = etree.SubElement(thead, f"{{{NS}}}tr")

        headers = ["Goal", "Status", "Start Date", "Target Date", "Value"]
        for header_text in headers:
            th = etree.SubElement(tr, f"{{{NS}}}th")
            th.text = header_text

        # Table body
        tbody = etree.SubElement(table, f"{{{NS}}}tbody")

        for idx, goal in enumerate(self.goals, start=1):
            tr = etree.SubElement(tbody, f"{{{NS}}}tr")

            # Goal description (with ID reference)
            td_desc = etree.SubElement(tr, f"{{{NS}}}td")
            content = etree.SubElement(
                td_desc,
                f"{{{NS}}}content",
                ID=f"goal-{idx}",
            )
            content.text = goal.description

            # Status
            td_status = etree.SubElement(tr, f"{{{NS}}}td")
            td_status.text = self._format_status(goal.status)

            # Start date
            td_start = etree.SubElement(tr, f"{{{NS}}}td")
            if goal.start_date:
                td_start.text = goal.start_date.strftime("%Y-%m-%d")
            else:
                td_start.text = "Not specified"

            # Target date
            td_target = etree.SubElement(tr, f"{{{NS}}}td")
            if goal.target_date:
                td_target.text = goal.target_date.strftime("%Y-%m-%d")
            else:
                td_target.text = "Not specified"

            # Value
            td_value = etree.SubElement(tr, f"{{{NS}}}td")
            if goal.value:
                if goal.value_unit:
                    td_value.text = f"{goal.value} {goal.value_unit}"
                else:
                    td_value.text = str(goal.value)
            else:
                td_value.text = "Not specified"

    def _add_entry(self, section: etree._Element, goal: GoalProtocol) -> None:
        """
        Add entry element with Goal Observation.

        Args:
            section: section element
            goal: Goal data
        """
        # Create entry element (SHALL contain at least one entry - CONF:1098-30719)
        entry = etree.SubElement(section, f"{{{NS}}}entry")

        # Create and add Goal Observation (CONF:1098-30720)
        obs_builder = GoalObservation(goal, version=self.version)
        entry.append(obs_builder.to_element())

    def _format_status(self, status: str) -> str:
        """
        Format status for display in narrative.

        Args:
            status: Raw status string

        Returns:
            Formatted status string
        """
        # Capitalize and replace hyphens with spaces
        formatted = status.replace("-", " ").replace("_", " ")
        return formatted.title()

Functions

__init__(goals, title='Goals', version=CDAVersion.R2_1, null_flavor=None, **kwargs)

Initialize GoalsSection builder.

Parameters:

Name Type Description Default
goals Sequence[GoalProtocol]

List of goals satisfying GoalProtocol

required
title str

Section title (default: "Goals")

'Goals'
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
null_flavor str

Optional null flavor (e.g., "NI" for no information)

None
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/goals.py
def __init__(
    self,
    goals: Sequence[GoalProtocol],
    title: str = "Goals",
    version: CDAVersion = CDAVersion.R2_1,
    null_flavor: str = None,
    **kwargs,
):
    """
    Initialize GoalsSection builder.

    Args:
        goals: List of goals satisfying GoalProtocol
        title: Section title (default: "Goals")
        version: C-CDA version (R2.1 or R2.0)
        null_flavor: Optional null flavor (e.g., "NI" for no information)
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.goals = goals
    self.title = title
    self.null_flavor = null_flavor

build()

Build Goals Section XML element.

Returns:

Type Description
Element

lxml Element for section

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

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add null flavor if specified (CONF:1098-32819)
    if self.null_flavor:
        section.set("nullFlavor", self.null_flavor)

    # Add template IDs (CONF:1098-29584, CONF:1098-29585)
    self.add_template_ids(section)

    # Add section code (CONF:1098-29586, CONF:1098-29587, CONF:1098-29588)
    # 61146-7 = Goals (LOINC)
    code_elem = Code(
        code="61146-7",
        system="LOINC",
        display_name="Goals",
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # Add title (CONF:1098-30721)
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (CONF:1098-30722)
    self._add_narrative(section)

    # Add entries with Goal Observations (CONF:1098-30719, CONF:1098-30720)
    if self.goals:
        for goal in self.goals:
            self._add_entry(section, goal)

    return section

HealthConcernsSection

HealthConcernsSection

Bases: CDAElement

Builder for C-CDA Health Concerns Section (V2).

Includes narrative (HTML table) and structured entries. Supports both R2.1 (2015-08-01) and R2.0 (2015-08-01) versions.

Per spec (2.16.840.1.113883.10.20.22.2.58): - code="75310-3" Health concerns document from LOINC (CONF:1198-28806) - SHALL contain title (CONF:1198-28809) - SHALL contain text (CONF:1198-28810) - If section/@nullFlavor is not present, SHALL contain at least one Health Concern Act entry (CONF:1198-30768) - MAY contain nullFlavor="NI" (CONF:1198-32802)

Source code in ccdakit/builders/sections/health_concerns.py
class HealthConcernsSection(CDAElement):
    """
    Builder for C-CDA Health Concerns Section (V2).

    Includes narrative (HTML table) and structured entries.
    Supports both R2.1 (2015-08-01) and R2.0 (2015-08-01) versions.

    Per spec (2.16.840.1.113883.10.20.22.2.58):
    - code="75310-3" Health concerns document from LOINC (CONF:1198-28806)
    - SHALL contain title (CONF:1198-28809)
    - SHALL contain text (CONF:1198-28810)
    - If section/@nullFlavor is not present, SHALL contain at least one
      Health Concern Act entry (CONF:1198-30768)
    - MAY contain nullFlavor="NI" (CONF:1198-32802)
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.58",
                extension="2015-08-01",
                description="Health Concerns Section V2",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.58",
                extension="2015-08-01",
                description="Health Concerns Section V2",
            ),
        ],
    }

    def __init__(
        self,
        health_concerns: Sequence[HealthConcernProtocol],
        title: str = "Health Concerns",
        null_flavor: Optional[str] = None,
        version: CDAVersion = CDAVersion.R2_1,
        **kwargs,
    ):
        """
        Initialize HealthConcernsSection builder.

        Args:
            health_concerns: List of health concerns satisfying HealthConcernProtocol
            title: Section title (default: "Health Concerns")
            null_flavor: Optional null flavor (e.g., "NI") if no information available
            version: C-CDA version (R2.1 or R2.0)
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.health_concerns = health_concerns
        self.title = title
        self.null_flavor = null_flavor

    def build(self) -> etree.Element:
        """
        Build Health Concerns Section XML element.

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add nullFlavor if specified (CONF:1198-32802)
        if self.null_flavor:
            section.set("nullFlavor", self.null_flavor)

        # Add template IDs (CONF:1198-28804)
        self.add_template_ids(section)

        # Add section code (75310-3 = Health concerns document) (CONF:1198-28806)
        code_elem = Code(
            code="75310-3",
            system="LOINC",
            display_name="Health concerns document",
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # Add title (CONF:1198-28809)
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (HTML table) (CONF:1198-28810)
        self._add_narrative(section)

        # Add entries with Health Concern Acts (CONF:1198-30768)
        # If nullFlavor is not present, SHALL contain at least one entry
        if not self.null_flavor:
            for health_concern in self.health_concerns:
                self._add_entry(section, health_concern)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element with HTML table.

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        if self.null_flavor or not self.health_concerns:
            # No health concerns - add "No health concerns" paragraph
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            if self.null_flavor == "NI":
                paragraph.text = "No information available"
            else:
                paragraph.text = "No health concerns"
            return

        # Create table for health concerns
        table = etree.SubElement(text, f"{{{NS}}}table", border="1", width="100%")

        # Table header
        thead = etree.SubElement(table, f"{{{NS}}}thead")
        tr = etree.SubElement(thead, f"{{{NS}}}tr")

        headers = [
            "Health Concern",
            "Status",
            "Effective Time",
            "Related Observations",
            "Concern Type",
        ]
        for header_text in headers:
            th = etree.SubElement(tr, f"{{{NS}}}th")
            th.text = header_text

        # Table body
        tbody = etree.SubElement(table, f"{{{NS}}}tbody")

        for idx, concern in enumerate(self.health_concerns, start=1):
            tr = etree.SubElement(tbody, f"{{{NS}}}tr")

            # Health concern name (with ID reference)
            td_name = etree.SubElement(tr, f"{{{NS}}}td")
            content = etree.SubElement(
                td_name,
                f"{{{NS}}}content",
                ID=f"health-concern-{idx}",
            )
            content.text = concern.name

            # Status
            td_status = etree.SubElement(tr, f"{{{NS}}}td")
            td_status.text = concern.status.capitalize()

            # Effective time
            td_time = etree.SubElement(tr, f"{{{NS}}}td")
            if concern.effective_time_low:
                time_str = concern.effective_time_low.strftime("%Y-%m-%d")
                if concern.effective_time_high:
                    time_str += f" to {concern.effective_time_high.strftime('%Y-%m-%d')}"
                else:
                    time_str += " - Ongoing"
                td_time.text = time_str
            else:
                td_time.text = "Unknown"

            # Related observations
            td_observations = etree.SubElement(tr, f"{{{NS}}}td")
            if concern.observations:
                obs_list = etree.SubElement(td_observations, f"{{{NS}}}list")
                for obs in concern.observations:
                    item = etree.SubElement(obs_list, f"{{{NS}}}item")
                    item.text = f"{obs.display_name} ({obs.observation_type})"
            else:
                td_observations.text = "None"

            # Concern type (patient vs provider)
            td_type = etree.SubElement(tr, f"{{{NS}}}td")
            td_type.text = "Patient" if concern.author_is_patient else "Provider"

    def _add_entry(self, section: etree._Element, health_concern: HealthConcernProtocol) -> None:
        """
        Add entry element with Health Concern Act.

        Per CONF:1198-30768, SHALL contain at least one [1..*] entry
        such that it SHALL contain exactly one [1..1] Health Concern Act (V2).

        Args:
            section: section element
            health_concern: Health concern data
        """
        # Create entry element
        entry = etree.SubElement(section, f"{{{NS}}}entry")

        # Create and add Health Concern Act
        act_builder = HealthConcernAct(health_concern, version=self.version)
        entry.append(act_builder.to_element())

Functions

__init__(health_concerns, title='Health Concerns', null_flavor=None, version=CDAVersion.R2_1, **kwargs)

Initialize HealthConcernsSection builder.

Parameters:

Name Type Description Default
health_concerns Sequence[HealthConcernProtocol]

List of health concerns satisfying HealthConcernProtocol

required
title str

Section title (default: "Health Concerns")

'Health Concerns'
null_flavor Optional[str]

Optional null flavor (e.g., "NI") if no information available

None
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/health_concerns.py
def __init__(
    self,
    health_concerns: Sequence[HealthConcernProtocol],
    title: str = "Health Concerns",
    null_flavor: Optional[str] = None,
    version: CDAVersion = CDAVersion.R2_1,
    **kwargs,
):
    """
    Initialize HealthConcernsSection builder.

    Args:
        health_concerns: List of health concerns satisfying HealthConcernProtocol
        title: Section title (default: "Health Concerns")
        null_flavor: Optional null flavor (e.g., "NI") if no information available
        version: C-CDA version (R2.1 or R2.0)
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.health_concerns = health_concerns
    self.title = title
    self.null_flavor = null_flavor

build()

Build Health Concerns Section XML element.

Returns:

Type Description
Element

lxml Element for section

Source code in ccdakit/builders/sections/health_concerns.py
def build(self) -> etree.Element:
    """
    Build Health Concerns Section XML element.

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add nullFlavor if specified (CONF:1198-32802)
    if self.null_flavor:
        section.set("nullFlavor", self.null_flavor)

    # Add template IDs (CONF:1198-28804)
    self.add_template_ids(section)

    # Add section code (75310-3 = Health concerns document) (CONF:1198-28806)
    code_elem = Code(
        code="75310-3",
        system="LOINC",
        display_name="Health concerns document",
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # Add title (CONF:1198-28809)
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (HTML table) (CONF:1198-28810)
    self._add_narrative(section)

    # Add entries with Health Concern Acts (CONF:1198-30768)
    # If nullFlavor is not present, SHALL contain at least one entry
    if not self.null_flavor:
        for health_concern in self.health_concerns:
            self._add_entry(section, health_concern)

    return section

HealthStatusEvaluationsSection

HealthStatusEvaluationsAndOutcomesSection

Bases: CDAElement

Builder for C-CDA Health Status Evaluations and Outcomes Section.

This section represents outcomes of the patient's health status. These assessed outcomes are represented as statuses, at points in time. It also includes outcomes of care from the interventions used to treat the patient, related to established care plan goals and/or interventions.

Includes narrative (HTML table) and structured entries. Supports both R2.1 and R2.0 versions.

Template ID: 2.16.840.1.113883.10.20.22.2.61

Conformance Rules: - MAY contain nullFlavor="NI" (CONF:1098-32821) - SHALL contain templateId (CONF:1098-29578, CONF:1098-29579) - SHALL contain code="11383-7" (CONF:1098-29580, CONF:1098-29581, CONF:1098-29582) - SHALL contain title (CONF:1098-29589) - SHALL contain text (CONF:1098-29590) - SHALL contain at least one [1..*] entry with Outcome Observation (CONF:1098-31227, CONF:1098-31228)

Source code in ccdakit/builders/sections/health_status_evaluations.py
class HealthStatusEvaluationsAndOutcomesSection(CDAElement):
    """
    Builder for C-CDA Health Status Evaluations and Outcomes Section.

    This section represents outcomes of the patient's health status. These assessed
    outcomes are represented as statuses, at points in time. It also includes
    outcomes of care from the interventions used to treat the patient, related to
    established care plan goals and/or interventions.

    Includes narrative (HTML table) and structured entries.
    Supports both R2.1 and R2.0 versions.

    Template ID: 2.16.840.1.113883.10.20.22.2.61

    Conformance Rules:
    - MAY contain nullFlavor="NI" (CONF:1098-32821)
    - SHALL contain templateId (CONF:1098-29578, CONF:1098-29579)
    - SHALL contain code="11383-7" (CONF:1098-29580, CONF:1098-29581, CONF:1098-29582)
    - SHALL contain title (CONF:1098-29589)
    - SHALL contain text (CONF:1098-29590)
    - SHALL contain at least one [1..*] entry with Outcome Observation (CONF:1098-31227, CONF:1098-31228)
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.61",
                extension=None,
                description="Health Status Evaluations and Outcomes Section",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.61",
                extension=None,
                description="Health Status Evaluations and Outcomes Section",
            ),
        ],
    }

    def __init__(
        self,
        outcomes: Sequence[OutcomeObservationProtocol],
        title: str = "Health Status Evaluations and Outcomes",
        version: CDAVersion = CDAVersion.R2_1,
        null_flavor: str = None,
        **kwargs,
    ):
        """
        Initialize HealthStatusEvaluationsAndOutcomesSection builder.

        Args:
            outcomes: List of outcomes satisfying OutcomeObservationProtocol
            title: Section title (default: "Health Status Evaluations and Outcomes")
            version: C-CDA version (R2.1 or R2.0)
            null_flavor: Optional null flavor (e.g., "NI" for no information)
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.outcomes = outcomes
        self.title = title
        self.null_flavor = null_flavor

    def build(self) -> etree.Element:
        """
        Build Health Status Evaluations and Outcomes Section XML element.

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add null flavor if specified (CONF:1098-32821)
        if self.null_flavor:
            section.set("nullFlavor", self.null_flavor)

        # Add template IDs (CONF:1098-29578, CONF:1098-29579)
        self.add_template_ids(section)

        # Add section code (CONF:1098-29580, CONF:1098-29581, CONF:1098-29582)
        # 11383-7 = Patient Problem Outcome (LOINC)
        code_elem = Code(
            code="11383-7",
            system="LOINC",
            display_name="Patient Problem Outcome",
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # Add title (CONF:1098-29589)
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (CONF:1098-29590)
        self._add_narrative(section)

        # Add entries with Outcome Observations (CONF:1098-31227, CONF:1098-31228)
        # SHALL contain at least one [1..*] entry
        if self.outcomes:
            for outcome in self.outcomes:
                self._add_entry(section, outcome)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element with HTML table.

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        if not self.outcomes:
            # No outcomes - add "No outcomes documented" paragraph
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            paragraph.text = "No health status evaluations or outcomes documented"
            return

        # Create table for outcomes
        table = etree.SubElement(text, f"{{{NS}}}table", border="1", width="100%")

        # Table header
        thead = etree.SubElement(table, f"{{{NS}}}thead")
        tr = etree.SubElement(thead, f"{{{NS}}}tr")

        headers = ["Outcome", "Value", "Date", "Progress Toward Goal"]
        for header_text in headers:
            th = etree.SubElement(tr, f"{{{NS}}}th")
            th.text = header_text

        # Table body
        tbody = etree.SubElement(table, f"{{{NS}}}tbody")

        for idx, outcome in enumerate(self.outcomes, start=1):
            tr = etree.SubElement(tbody, f"{{{NS}}}tr")

            # Outcome description (with ID reference)
            td_desc = etree.SubElement(tr, f"{{{NS}}}td")
            content = etree.SubElement(
                td_desc,
                f"{{{NS}}}content",
                ID=f"outcome-{idx}",
            )
            # Use display name or code
            if outcome.display_name:
                content.text = outcome.display_name
            elif outcome.code:
                content.text = f"Outcome: {outcome.code}"
            else:
                content.text = "Outcome observation"

            # Value
            td_value = etree.SubElement(tr, f"{{{NS}}}td")
            if outcome.value:
                if hasattr(outcome, "value_unit") and outcome.value_unit:
                    td_value.text = f"{outcome.value} {outcome.value_unit}"
                else:
                    td_value.text = str(outcome.value)
            else:
                td_value.text = "Not specified"

            # Effective time
            td_time = etree.SubElement(tr, f"{{{NS}}}td")
            if hasattr(outcome, "effective_time") and outcome.effective_time:
                if hasattr(outcome.effective_time, "strftime"):
                    td_time.text = outcome.effective_time.strftime("%Y-%m-%d")
                else:
                    td_time.text = str(outcome.effective_time)
            else:
                td_time.text = "Not specified"

            # Progress toward goal
            td_progress = etree.SubElement(tr, f"{{{NS}}}td")
            if (
                hasattr(outcome, "progress_toward_goal")
                and outcome.progress_toward_goal
                and hasattr(outcome.progress_toward_goal, "achievement_display_name")
            ):
                td_progress.text = (
                    outcome.progress_toward_goal.achievement_display_name or "Progress documented"
                )
            else:
                td_progress.text = "Not specified"

    def _add_entry(self, section: etree._Element, outcome: OutcomeObservationProtocol) -> None:
        """
        Add entry element with Outcome Observation.

        Args:
            section: section element
            outcome: Outcome data
        """
        # Create entry element (SHALL contain at least one entry - CONF:1098-31227)
        entry = etree.SubElement(section, f"{{{NS}}}entry")

        # Create and add Outcome Observation (CONF:1098-31228)
        obs_builder = OutcomeObservation(outcome, version=self.version)
        entry.append(obs_builder.to_element())

Functions

__init__(outcomes, title='Health Status Evaluations and Outcomes', version=CDAVersion.R2_1, null_flavor=None, **kwargs)

Initialize HealthStatusEvaluationsAndOutcomesSection builder.

Parameters:

Name Type Description Default
outcomes Sequence[OutcomeObservationProtocol]

List of outcomes satisfying OutcomeObservationProtocol

required
title str

Section title (default: "Health Status Evaluations and Outcomes")

'Health Status Evaluations and Outcomes'
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
null_flavor str

Optional null flavor (e.g., "NI" for no information)

None
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/health_status_evaluations.py
def __init__(
    self,
    outcomes: Sequence[OutcomeObservationProtocol],
    title: str = "Health Status Evaluations and Outcomes",
    version: CDAVersion = CDAVersion.R2_1,
    null_flavor: str = None,
    **kwargs,
):
    """
    Initialize HealthStatusEvaluationsAndOutcomesSection builder.

    Args:
        outcomes: List of outcomes satisfying OutcomeObservationProtocol
        title: Section title (default: "Health Status Evaluations and Outcomes")
        version: C-CDA version (R2.1 or R2.0)
        null_flavor: Optional null flavor (e.g., "NI" for no information)
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.outcomes = outcomes
    self.title = title
    self.null_flavor = null_flavor

build()

Build Health Status Evaluations and Outcomes Section XML element.

Returns:

Type Description
Element

lxml Element for section

Source code in ccdakit/builders/sections/health_status_evaluations.py
def build(self) -> etree.Element:
    """
    Build Health Status Evaluations and Outcomes Section XML element.

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add null flavor if specified (CONF:1098-32821)
    if self.null_flavor:
        section.set("nullFlavor", self.null_flavor)

    # Add template IDs (CONF:1098-29578, CONF:1098-29579)
    self.add_template_ids(section)

    # Add section code (CONF:1098-29580, CONF:1098-29581, CONF:1098-29582)
    # 11383-7 = Patient Problem Outcome (LOINC)
    code_elem = Code(
        code="11383-7",
        system="LOINC",
        display_name="Patient Problem Outcome",
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # Add title (CONF:1098-29589)
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (CONF:1098-29590)
    self._add_narrative(section)

    # Add entries with Outcome Observations (CONF:1098-31227, CONF:1098-31228)
    # SHALL contain at least one [1..*] entry
    if self.outcomes:
        for outcome in self.outcomes:
            self._add_entry(section, outcome)

    return section

HospitalCourseSection

HospitalCourseSection

Bases: CDAElement

Builder for C-CDA Hospital Course Section.

This section describes the sequence of events from admission to discharge in a hospital facility. It is a narrative-only section that provides a chronological account of the patient's hospital stay.

The Hospital Course Section is typically included in Discharge Summary documents to document the patient's clinical course during hospitalization, including significant events, treatments, procedures, and response to therapy.

This is a narrative-only section with no structured entries.

Conformance: - Template ID: 1.3.6.1.4.1.19376.1.5.3.1.3.5 (IHE) - Code: 8648-8 (Hospital Course) from LOINC - Contains: Narrative text only (no entries required)

References: - CONF:81-7852: SHALL contain templateId - CONF:81-10459: templateId/@root="1.3.6.1.4.1.19376.1.5.3.1.3.5" - CONF:81-15487: SHALL contain code - CONF:81-15488: code/@code="8648-8" - CONF:81-26480: code/@codeSystem="2.16.840.1.113883.6.1" (LOINC) - CONF:81-7854: SHALL contain title - CONF:81-7855: SHALL contain text

Source code in ccdakit/builders/sections/hospital_course.py
class HospitalCourseSection(CDAElement):
    """
    Builder for C-CDA Hospital Course Section.

    This section describes the sequence of events from admission to discharge
    in a hospital facility. It is a narrative-only section that provides a
    chronological account of the patient's hospital stay.

    The Hospital Course Section is typically included in Discharge Summary
    documents to document the patient's clinical course during hospitalization,
    including significant events, treatments, procedures, and response to therapy.

    This is a narrative-only section with no structured entries.

    Conformance:
    - Template ID: 1.3.6.1.4.1.19376.1.5.3.1.3.5 (IHE)
    - Code: 8648-8 (Hospital Course) from LOINC
    - Contains: Narrative text only (no entries required)

    References:
    - CONF:81-7852: SHALL contain templateId
    - CONF:81-10459: templateId/@root="1.3.6.1.4.1.19376.1.5.3.1.3.5"
    - CONF:81-15487: SHALL contain code
    - CONF:81-15488: code/@code="8648-8"
    - CONF:81-26480: code/@codeSystem="2.16.840.1.113883.6.1" (LOINC)
    - CONF:81-7854: SHALL contain title
    - CONF:81-7855: SHALL contain text
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="1.3.6.1.4.1.19376.1.5.3.1.3.5",
                extension=None,  # No extension in spec
                description="Hospital Course Section (IHE)",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="1.3.6.1.4.1.19376.1.5.3.1.3.5",
                extension=None,
                description="Hospital Course Section (IHE)",
            ),
        ],
    }

    def __init__(
        self,
        hospital_course: Optional[HospitalCourseProtocol] = None,
        narrative_text: Optional[str] = None,
        title: str = "Hospital Course",
        version: CDAVersion = CDAVersion.R2_1,
        **kwargs,
    ):
        """
        Initialize HospitalCourseSection builder.

        You can provide either:
        1. A HospitalCourseProtocol object with course_text property
        2. A narrative_text string directly
        3. Both (narrative_text takes precedence)

        Args:
            hospital_course: Hospital course data object implementing HospitalCourseProtocol
            narrative_text: Free-form narrative text for hospital course (takes precedence)
            title: Section title (default: "Hospital Course")
            version: C-CDA version (R2.1 or R2.0)
            **kwargs: Additional arguments passed to CDAElement

        Raises:
            ValueError: If neither hospital_course nor narrative_text is provided
        """
        super().__init__(version=version, **kwargs)
        self.hospital_course = hospital_course
        self.narrative_text = narrative_text
        self.title = title

        # Determine the final narrative text to use
        if self.narrative_text:
            self._final_narrative = self.narrative_text
        elif self.hospital_course:
            self._final_narrative = self.hospital_course.course_text
        else:
            # Allow empty content - will show default message
            self._final_narrative = "No hospital course information provided."

    def build(self) -> etree.Element:
        """
        Build Hospital Course Section XML element.

        Conformance:
        - CONF:81-7852: SHALL contain templateId
        - CONF:81-10459: templateId/@root="1.3.6.1.4.1.19376.1.5.3.1.3.5"
        - CONF:81-15487: SHALL contain code
        - CONF:81-15488: code/@code="8648-8"
        - CONF:81-26480: code/@codeSystem="2.16.840.1.113883.6.1" (LOINC)
        - CONF:81-7854: SHALL contain title
        - CONF:81-7855: SHALL contain text

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add template IDs (CONF:81-7852, CONF:81-10459)
        self.add_template_ids(section)

        # Add section code (CONF:81-15487, CONF:81-15488, CONF:81-26480)
        # 8648-8 = Hospital Course (LOINC)
        code_elem = Code(
            code="8648-8",
            system="LOINC",
            display_name="Hospital Course",
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # Add title (CONF:81-7854)
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (CONF:81-7855)
        self._add_narrative(section)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element with hospital course content.

        The narrative contains the chronological description of the patient's
        hospital stay. For long narratives, multiple paragraphs may be used
        to improve readability.

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        # Split narrative into paragraphs if it contains double line breaks
        # This helps with readability for longer hospital courses
        if "\n\n" in self._final_narrative:
            paragraphs = self._final_narrative.split("\n\n")
            for para_text in paragraphs:
                if para_text.strip():  # Only add non-empty paragraphs
                    paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
                    paragraph.text = para_text.strip()
        else:
            # Single paragraph for simpler narratives
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            paragraph.text = self._final_narrative

Functions

__init__(hospital_course=None, narrative_text=None, title='Hospital Course', version=CDAVersion.R2_1, **kwargs)

Initialize HospitalCourseSection builder.

You can provide either: 1. A HospitalCourseProtocol object with course_text property 2. A narrative_text string directly 3. Both (narrative_text takes precedence)

Parameters:

Name Type Description Default
hospital_course Optional[HospitalCourseProtocol]

Hospital course data object implementing HospitalCourseProtocol

None
narrative_text Optional[str]

Free-form narrative text for hospital course (takes precedence)

None
title str

Section title (default: "Hospital Course")

'Hospital Course'
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
**kwargs

Additional arguments passed to CDAElement

{}

Raises:

Type Description
ValueError

If neither hospital_course nor narrative_text is provided

Source code in ccdakit/builders/sections/hospital_course.py
def __init__(
    self,
    hospital_course: Optional[HospitalCourseProtocol] = None,
    narrative_text: Optional[str] = None,
    title: str = "Hospital Course",
    version: CDAVersion = CDAVersion.R2_1,
    **kwargs,
):
    """
    Initialize HospitalCourseSection builder.

    You can provide either:
    1. A HospitalCourseProtocol object with course_text property
    2. A narrative_text string directly
    3. Both (narrative_text takes precedence)

    Args:
        hospital_course: Hospital course data object implementing HospitalCourseProtocol
        narrative_text: Free-form narrative text for hospital course (takes precedence)
        title: Section title (default: "Hospital Course")
        version: C-CDA version (R2.1 or R2.0)
        **kwargs: Additional arguments passed to CDAElement

    Raises:
        ValueError: If neither hospital_course nor narrative_text is provided
    """
    super().__init__(version=version, **kwargs)
    self.hospital_course = hospital_course
    self.narrative_text = narrative_text
    self.title = title

    # Determine the final narrative text to use
    if self.narrative_text:
        self._final_narrative = self.narrative_text
    elif self.hospital_course:
        self._final_narrative = self.hospital_course.course_text
    else:
        # Allow empty content - will show default message
        self._final_narrative = "No hospital course information provided."

build()

Build Hospital Course Section XML element.

Conformance: - CONF:81-7852: SHALL contain templateId - CONF:81-10459: templateId/@root="1.3.6.1.4.1.19376.1.5.3.1.3.5" - CONF:81-15487: SHALL contain code - CONF:81-15488: code/@code="8648-8" - CONF:81-26480: code/@codeSystem="2.16.840.1.113883.6.1" (LOINC) - CONF:81-7854: SHALL contain title - CONF:81-7855: SHALL contain text

Returns:

Type Description
Element

lxml Element for section

Source code in ccdakit/builders/sections/hospital_course.py
def build(self) -> etree.Element:
    """
    Build Hospital Course Section XML element.

    Conformance:
    - CONF:81-7852: SHALL contain templateId
    - CONF:81-10459: templateId/@root="1.3.6.1.4.1.19376.1.5.3.1.3.5"
    - CONF:81-15487: SHALL contain code
    - CONF:81-15488: code/@code="8648-8"
    - CONF:81-26480: code/@codeSystem="2.16.840.1.113883.6.1" (LOINC)
    - CONF:81-7854: SHALL contain title
    - CONF:81-7855: SHALL contain text

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add template IDs (CONF:81-7852, CONF:81-10459)
    self.add_template_ids(section)

    # Add section code (CONF:81-15487, CONF:81-15488, CONF:81-26480)
    # 8648-8 = Hospital Course (LOINC)
    code_elem = Code(
        code="8648-8",
        system="LOINC",
        display_name="Hospital Course",
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # Add title (CONF:81-7854)
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (CONF:81-7855)
    self._add_narrative(section)

    return section

HospitalDischargeInstructionsSection

HospitalDischargeInstructionsSection

Bases: CDAElement

Builder for C-CDA Hospital Discharge Instructions Section.

This section records instructions provided to the patient at hospital discharge. It contains only narrative text (no structured entries are required or typically used).

The section can contain general discharge instructions or categorized instructions (e.g., medications, diet, activity, follow-up care).

Supports both R2.1 and R2.0 versions.

Conformance: - Template ID: 2.16.840.1.113883.10.20.22.2.41 - Code: 8653-8 (Hospital Discharge Instructions) from LOINC - Contains: Narrative text only (no entries required)

References: - CONF:81-9919: SHALL contain templateId - CONF:81-10395: templateId/@root="2.16.840.1.113883.10.20.22.2.41" - CONF:81-15357: SHALL contain code - CONF:81-15358: code/@code="8653-8" - CONF:81-26481: code/@codeSystem="2.16.840.1.113883.6.1" (LOINC) - CONF:81-9921: SHALL contain title - CONF:81-9922: SHALL contain text

Source code in ccdakit/builders/sections/hospital_discharge_instructions.py
class HospitalDischargeInstructionsSection(CDAElement):
    """
    Builder for C-CDA Hospital Discharge Instructions Section.

    This section records instructions provided to the patient at hospital discharge.
    It contains only narrative text (no structured entries are required or typically used).

    The section can contain general discharge instructions or categorized instructions
    (e.g., medications, diet, activity, follow-up care).

    Supports both R2.1 and R2.0 versions.

    Conformance:
    - Template ID: 2.16.840.1.113883.10.20.22.2.41
    - Code: 8653-8 (Hospital Discharge Instructions) from LOINC
    - Contains: Narrative text only (no entries required)

    References:
    - CONF:81-9919: SHALL contain templateId
    - CONF:81-10395: templateId/@root="2.16.840.1.113883.10.20.22.2.41"
    - CONF:81-15357: SHALL contain code
    - CONF:81-15358: code/@code="8653-8"
    - CONF:81-26481: code/@codeSystem="2.16.840.1.113883.6.1" (LOINC)
    - CONF:81-9921: SHALL contain title
    - CONF:81-9922: SHALL contain text
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.41",
                extension=None,  # No extension in spec
                description="Hospital Discharge Instructions Section R2.1",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.41",
                extension=None,
                description="Hospital Discharge Instructions Section R2.0",
            ),
        ],
    }

    def __init__(
        self,
        instructions: Optional[Sequence[DischargeInstructionProtocol]] = None,
        narrative_text: Optional[str] = None,
        title: str = "Hospital Discharge Instructions",
        version: CDAVersion = CDAVersion.R2_1,
        **kwargs,
    ):
        """
        Initialize HospitalDischargeInstructionsSection builder.

        You can provide either:
        1. A list of DischargeInstructionProtocol objects (will be formatted as a list/table)
        2. A narrative_text string (will be used as-is in a paragraph)
        3. Both (instructions will be formatted as a table, narrative_text as preamble)

        Args:
            instructions: List of discharge instructions (optional)
            narrative_text: Free-form narrative text for instructions (optional)
            title: Section title (default: "Hospital Discharge Instructions")
            version: C-CDA version (R2.1 or R2.0)
            **kwargs: Additional arguments passed to CDAElement

        Raises:
            ValueError: If neither instructions nor narrative_text is provided
        """
        super().__init__(version=version, **kwargs)
        self.instructions = instructions or []
        self.narrative_text = narrative_text
        self.title = title

        # At least one source of content must be provided
        if not self.instructions and not self.narrative_text:
            # Allow empty content - will show default message
            self.narrative_text = "No discharge instructions provided."

    def build(self) -> etree.Element:
        """
        Build Hospital Discharge Instructions Section XML element.

        Conformance:
        - CONF:81-9919: SHALL contain templateId
        - CONF:81-10395: templateId/@root="2.16.840.1.113883.10.20.22.2.41"
        - CONF:81-15357: SHALL contain code
        - CONF:81-15358: code/@code="8653-8"
        - CONF:81-26481: code/@codeSystem="2.16.840.1.113883.6.1" (LOINC)
        - CONF:81-9921: SHALL contain title
        - CONF:81-9922: SHALL contain text

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add template IDs (CONF:81-9919, CONF:81-10395)
        self.add_template_ids(section)

        # Add section code (CONF:81-15357, CONF:81-15358, CONF:81-26481)
        # 8653-8 = Hospital Discharge Instructions (LOINC)
        code_elem = Code(
            code="8653-8",
            system="LOINC",
            display_name="Hospital Discharge Instructions",
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # Add title (CONF:81-9921)
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (CONF:81-9922)
        self._add_narrative(section)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element with formatted content.

        The narrative can contain:
        1. Simple paragraph text (if narrative_text is provided)
        2. Categorized list (if instructions with categories are provided)
        3. Simple list (if instructions without categories are provided)
        4. Both paragraph and list

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        # Add preamble paragraph if narrative_text is provided
        if self.narrative_text:
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            paragraph.text = self.narrative_text

        # Add instructions if provided
        if self.instructions:
            self._add_instructions_narrative(text)

    def _add_instructions_narrative(self, text: etree._Element) -> None:
        """
        Add instructions to narrative text.

        If instructions have categories, they're grouped by category with headers.
        Otherwise, they're shown as a simple list.

        Args:
            text: text element
        """
        # Check if any instructions have categories
        has_categories = any(instr.instruction_category for instr in self.instructions)

        if has_categories:
            self._add_categorized_instructions(text)
        else:
            self._add_simple_instructions_list(text)

    def _add_categorized_instructions(self, text: etree._Element) -> None:
        """
        Add instructions grouped by category with section headers.

        Args:
            text: text element
        """
        # Group instructions by category
        categorized = {}
        uncategorized = []

        for instr in self.instructions:
            if instr.instruction_category:
                category = instr.instruction_category
                if category not in categorized:
                    categorized[category] = []
                categorized[category].append(instr)
            else:
                uncategorized.append(instr)

        # Add categorized sections
        for category in sorted(categorized.keys()):
            # Add category header
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            content = etree.SubElement(paragraph, f"{{{NS}}}content")
            content.set("styleCode", "Bold")
            content.text = category

            # Add instructions as list
            list_elem = etree.SubElement(text, f"{{{NS}}}list")
            list_elem.set("listType", "unordered")

            for instr in categorized[category]:
                item = etree.SubElement(list_elem, f"{{{NS}}}item")
                item.text = instr.instruction_text

        # Add uncategorized instructions if any
        if uncategorized:
            if categorized:  # Add header only if we had categories
                paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
                content = etree.SubElement(paragraph, f"{{{NS}}}content")
                content.set("styleCode", "Bold")
                content.text = "General Instructions"

            list_elem = etree.SubElement(text, f"{{{NS}}}list")
            list_elem.set("listType", "unordered")

            for instr in uncategorized:
                item = etree.SubElement(list_elem, f"{{{NS}}}item")
                item.text = instr.instruction_text

    def _add_simple_instructions_list(self, text: etree._Element) -> None:
        """
        Add instructions as a simple unordered list.

        Args:
            text: text element
        """
        list_elem = etree.SubElement(text, f"{{{NS}}}list")
        list_elem.set("listType", "unordered")

        for instr in self.instructions:
            item = etree.SubElement(list_elem, f"{{{NS}}}item")
            item.text = instr.instruction_text

Functions

__init__(instructions=None, narrative_text=None, title='Hospital Discharge Instructions', version=CDAVersion.R2_1, **kwargs)

Initialize HospitalDischargeInstructionsSection builder.

You can provide either: 1. A list of DischargeInstructionProtocol objects (will be formatted as a list/table) 2. A narrative_text string (will be used as-is in a paragraph) 3. Both (instructions will be formatted as a table, narrative_text as preamble)

Parameters:

Name Type Description Default
instructions Optional[Sequence[DischargeInstructionProtocol]]

List of discharge instructions (optional)

None
narrative_text Optional[str]

Free-form narrative text for instructions (optional)

None
title str

Section title (default: "Hospital Discharge Instructions")

'Hospital Discharge Instructions'
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
**kwargs

Additional arguments passed to CDAElement

{}

Raises:

Type Description
ValueError

If neither instructions nor narrative_text is provided

Source code in ccdakit/builders/sections/hospital_discharge_instructions.py
def __init__(
    self,
    instructions: Optional[Sequence[DischargeInstructionProtocol]] = None,
    narrative_text: Optional[str] = None,
    title: str = "Hospital Discharge Instructions",
    version: CDAVersion = CDAVersion.R2_1,
    **kwargs,
):
    """
    Initialize HospitalDischargeInstructionsSection builder.

    You can provide either:
    1. A list of DischargeInstructionProtocol objects (will be formatted as a list/table)
    2. A narrative_text string (will be used as-is in a paragraph)
    3. Both (instructions will be formatted as a table, narrative_text as preamble)

    Args:
        instructions: List of discharge instructions (optional)
        narrative_text: Free-form narrative text for instructions (optional)
        title: Section title (default: "Hospital Discharge Instructions")
        version: C-CDA version (R2.1 or R2.0)
        **kwargs: Additional arguments passed to CDAElement

    Raises:
        ValueError: If neither instructions nor narrative_text is provided
    """
    super().__init__(version=version, **kwargs)
    self.instructions = instructions or []
    self.narrative_text = narrative_text
    self.title = title

    # At least one source of content must be provided
    if not self.instructions and not self.narrative_text:
        # Allow empty content - will show default message
        self.narrative_text = "No discharge instructions provided."

build()

Build Hospital Discharge Instructions Section XML element.

Conformance: - CONF:81-9919: SHALL contain templateId - CONF:81-10395: templateId/@root="2.16.840.1.113883.10.20.22.2.41" - CONF:81-15357: SHALL contain code - CONF:81-15358: code/@code="8653-8" - CONF:81-26481: code/@codeSystem="2.16.840.1.113883.6.1" (LOINC) - CONF:81-9921: SHALL contain title - CONF:81-9922: SHALL contain text

Returns:

Type Description
Element

lxml Element for section

Source code in ccdakit/builders/sections/hospital_discharge_instructions.py
def build(self) -> etree.Element:
    """
    Build Hospital Discharge Instructions Section XML element.

    Conformance:
    - CONF:81-9919: SHALL contain templateId
    - CONF:81-10395: templateId/@root="2.16.840.1.113883.10.20.22.2.41"
    - CONF:81-15357: SHALL contain code
    - CONF:81-15358: code/@code="8653-8"
    - CONF:81-26481: code/@codeSystem="2.16.840.1.113883.6.1" (LOINC)
    - CONF:81-9921: SHALL contain title
    - CONF:81-9922: SHALL contain text

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add template IDs (CONF:81-9919, CONF:81-10395)
    self.add_template_ids(section)

    # Add section code (CONF:81-15357, CONF:81-15358, CONF:81-26481)
    # 8653-8 = Hospital Discharge Instructions (LOINC)
    code_elem = Code(
        code="8653-8",
        system="LOINC",
        display_name="Hospital Discharge Instructions",
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # Add title (CONF:81-9921)
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (CONF:81-9922)
    self._add_narrative(section)

    return section

HospitalDischargeStudiesSummarySection

HospitalDischargeStudiesSummarySection

Bases: CDAElement

Builder for C-CDA Hospital Discharge Studies Summary Section.

This section records the results of observations generated by laboratories, imaging procedures, and other procedures at discharge. The scope includes hematology, chemistry, serology, virology, toxicology, microbiology, plain x-ray, ultrasound, CT, MRI, angiography, echocardiography, nuclear medicine, pathology, and procedure observations.

Implements Hospital Discharge Studies Summary Section (template 2.16.840.1.113883.10.20.22.2.16).

Key features: - Groups discharge studies by panel/organizer - Displays study name, value, unit, interpretation, and reference range - Supports LOINC codes for study identification - Reuses Result Organizer entry pattern for consistency

Source code in ccdakit/builders/sections/discharge_studies.py
class HospitalDischargeStudiesSummarySection(CDAElement):
    """
    Builder for C-CDA Hospital Discharge Studies Summary Section.

    This section records the results of observations generated by laboratories,
    imaging procedures, and other procedures at discharge. The scope includes
    hematology, chemistry, serology, virology, toxicology, microbiology, plain
    x-ray, ultrasound, CT, MRI, angiography, echocardiography, nuclear medicine,
    pathology, and procedure observations.

    Implements Hospital Discharge Studies Summary Section (template 2.16.840.1.113883.10.20.22.2.16).

    Key features:
    - Groups discharge studies by panel/organizer
    - Displays study name, value, unit, interpretation, and reference range
    - Supports LOINC codes for study identification
    - Reuses Result Organizer entry pattern for consistency
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.16",
                extension=None,
                description="Hospital Discharge Studies Summary Section",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.16",
                extension=None,
                description="Hospital Discharge Studies Summary Section",
            ),
        ],
    }

    def __init__(
        self,
        study_organizers: Sequence[DischargeStudyOrganizerProtocol],
        title: str = "Hospital Discharge Studies Summary",
        version: CDAVersion = CDAVersion.R2_1,
        **kwargs,
    ):
        """
        Initialize HospitalDischargeStudiesSummarySection builder.

        Args:
            study_organizers: List of discharge study organizers (study panels)
            title: Section title (default: "Hospital Discharge Studies Summary")
            version: C-CDA version (R2.1 or R2.0)
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.study_organizers = study_organizers
        self.title = title

    def build(self) -> etree.Element:
        """
        Build Hospital Discharge Studies Summary Section XML element.

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add template IDs
        self.add_template_ids(section)

        # Add section code (11493-4 = Hospital Discharge Studies Summary)
        code_elem = Code(
            code="11493-4",
            system="LOINC",
            display_name="Hospital Discharge Studies Summary",
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # Add title
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (HTML table)
        self._add_narrative(section)

        # Add entries with Result Organizers (reusing the pattern)
        for organizer in self.study_organizers:
            self._add_entry(section, organizer)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element with HTML table.

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        if not self.study_organizers:
            # No studies - add "No discharge studies available" paragraph
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            paragraph.text = "No discharge studies available"
            return

        # Create table for studies
        table = etree.SubElement(text, f"{{{NS}}}table", border="1", width="100%")

        # Table header
        thead = etree.SubElement(table, f"{{{NS}}}thead")
        tr = etree.SubElement(thead, f"{{{NS}}}tr")

        headers = [
            "Study Panel",
            "Study",
            "Value",
            "Unit",
            "Interpretation",
            "Reference Range",
            "Date",
        ]
        for header_text in headers:
            th = etree.SubElement(tr, f"{{{NS}}}th")
            th.text = header_text

        # Table body
        tbody = etree.SubElement(table, f"{{{NS}}}tbody")

        for organizer_idx, organizer in enumerate(self.study_organizers, start=1):
            # Add each study in the organizer
            for study_idx, study in enumerate(organizer.studies, start=1):
                tr = etree.SubElement(tbody, f"{{{NS}}}tr")

                # Study panel name (only on first row of each panel)
                td_panel = etree.SubElement(tr, f"{{{NS}}}td")
                if study_idx == 1:
                    content_panel = etree.SubElement(
                        td_panel,
                        f"{{{NS}}}content",
                        ID=f"discharge-study-panel-{organizer_idx}",
                    )
                    content_panel.text = organizer.study_panel_name
                else:
                    td_panel.text = ""  # Empty for subsequent rows

                # Study name (with ID reference)
                td_study = etree.SubElement(tr, f"{{{NS}}}td")
                content_study = etree.SubElement(
                    td_study,
                    f"{{{NS}}}content",
                    ID=f"discharge-study-{organizer_idx}-{study_idx}",
                )
                content_study.text = study.study_name

                # Value
                td_value = etree.SubElement(tr, f"{{{NS}}}td")
                td_value.text = study.value

                # Unit
                td_unit = etree.SubElement(tr, f"{{{NS}}}td")
                if study.unit:
                    td_unit.text = study.unit
                else:
                    td_unit.text = "-"

                # Interpretation
                td_interpretation = etree.SubElement(tr, f"{{{NS}}}td")
                if study.interpretation:
                    td_interpretation.text = study.interpretation
                else:
                    td_interpretation.text = "-"

                # Reference Range
                td_range = etree.SubElement(tr, f"{{{NS}}}td")
                if study.reference_range_low or study.reference_range_high:
                    range_text = []
                    if study.reference_range_low:
                        range_text.append(study.reference_range_low)
                    if study.reference_range_high:
                        if range_text:
                            range_text.append(f" - {study.reference_range_high}")
                        else:
                            range_text.append(f"< {study.reference_range_high}")
                    if study.reference_range_unit:
                        range_text.append(f" {study.reference_range_unit}")
                    td_range.text = "".join(range_text)
                else:
                    td_range.text = "-"

                # Date (from organizer or study)
                td_date = etree.SubElement(tr, f"{{{NS}}}td")
                # Use organizer date for consistency
                td_date.text = organizer.effective_time.strftime("%Y-%m-%d")

    def _add_entry(
        self, section: etree._Element, organizer: DischargeStudyOrganizerProtocol
    ) -> None:
        """
        Add entry element with Result Organizer.

        This reuses the Result Organizer entry builder since discharge studies
        follow the same pattern as lab results.

        Args:
            section: section element
            organizer: Discharge study organizer data
        """
        # Create entry element
        entry = etree.SubElement(section, f"{{{NS}}}entry", typeCode="DRIV")

        # Create and add Result Organizer (reusing the pattern)
        # The DischargeStudyOrganizerProtocol is compatible with ResultOrganizerProtocol
        organizer_builder = ResultOrganizer(organizer, version=self.version)  # type: ignore
        entry.append(organizer_builder.to_element())

Functions

__init__(study_organizers, title='Hospital Discharge Studies Summary', version=CDAVersion.R2_1, **kwargs)

Initialize HospitalDischargeStudiesSummarySection builder.

Parameters:

Name Type Description Default
study_organizers Sequence[DischargeStudyOrganizerProtocol]

List of discharge study organizers (study panels)

required
title str

Section title (default: "Hospital Discharge Studies Summary")

'Hospital Discharge Studies Summary'
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/discharge_studies.py
def __init__(
    self,
    study_organizers: Sequence[DischargeStudyOrganizerProtocol],
    title: str = "Hospital Discharge Studies Summary",
    version: CDAVersion = CDAVersion.R2_1,
    **kwargs,
):
    """
    Initialize HospitalDischargeStudiesSummarySection builder.

    Args:
        study_organizers: List of discharge study organizers (study panels)
        title: Section title (default: "Hospital Discharge Studies Summary")
        version: C-CDA version (R2.1 or R2.0)
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.study_organizers = study_organizers
    self.title = title

build()

Build Hospital Discharge Studies Summary Section XML element.

Returns:

Type Description
Element

lxml Element for section

Source code in ccdakit/builders/sections/discharge_studies.py
def build(self) -> etree.Element:
    """
    Build Hospital Discharge Studies Summary Section XML element.

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add template IDs
    self.add_template_ids(section)

    # Add section code (11493-4 = Hospital Discharge Studies Summary)
    code_elem = Code(
        code="11493-4",
        system="LOINC",
        display_name="Hospital Discharge Studies Summary",
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # Add title
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (HTML table)
    self._add_narrative(section)

    # Add entries with Result Organizers (reusing the pattern)
    for organizer in self.study_organizers:
        self._add_entry(section, organizer)

    return section

ImmunizationsSection

ImmunizationsSection

Bases: CDAElement

Builder for C-CDA Immunizations Section.

Includes narrative (HTML table) and structured entries. Supports both R2.1 (2015-08-01) and R2.0 (2014-06-09) versions.

Source code in ccdakit/builders/sections/immunizations.py
class ImmunizationsSection(CDAElement):
    """
    Builder for C-CDA Immunizations Section.

    Includes narrative (HTML table) and structured entries.
    Supports both R2.1 (2015-08-01) and R2.0 (2014-06-09) versions.
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.2.1",
                extension="2015-08-01",
                description="Immunizations Section (entries required) R2.1",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.2.1",
                extension="2014-06-09",
                description="Immunizations Section (entries required) R2.0",
            ),
        ],
    }

    def __init__(
        self,
        immunizations: Sequence[ImmunizationProtocol],
        title: str = "Immunizations",
        version: CDAVersion = CDAVersion.R2_1,
        **kwargs,
    ):
        """
        Initialize ImmunizationsSection builder.

        Args:
            immunizations: List of immunizations satisfying ImmunizationProtocol
            title: Section title (default: "Immunizations")
            version: C-CDA version (R2.1 or R2.0)
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.immunizations = immunizations
        self.title = title

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

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add template IDs
        self.add_template_ids(section)

        # Add section code (11369-6 = History of Immunization Narrative)
        code_elem = Code(
            code="11369-6",
            system="LOINC",
            display_name="History of Immunization Narrative",
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # Add title
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (HTML table)
        self._add_narrative(section)

        # Add entries with Immunization Activities
        for immunization in self.immunizations:
            self._add_entry(section, immunization)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element with HTML table.

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        if not self.immunizations:
            # No immunizations - add "No known immunizations" paragraph
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            paragraph.text = "No known immunizations"
            return

        # Create table for immunizations
        table = etree.SubElement(text, f"{{{NS}}}table", border="1", width="100%")

        # Table header
        thead = etree.SubElement(table, f"{{{NS}}}thead")
        tr = etree.SubElement(thead, f"{{{NS}}}tr")

        headers = [
            "Vaccine",
            "Date",
            "Status",
            "Lot Number",
            "Manufacturer",
        ]
        for header_text in headers:
            th = etree.SubElement(tr, f"{{{NS}}}th")
            th.text = header_text

        # Table body
        tbody = etree.SubElement(table, f"{{{NS}}}tbody")

        for idx, immunization in enumerate(self.immunizations, start=1):
            tr = etree.SubElement(tbody, f"{{{NS}}}tr")

            # Vaccine name (with ID reference)
            td_name = etree.SubElement(tr, f"{{{NS}}}td")
            content = etree.SubElement(
                td_name,
                f"{{{NS}}}content",
                ID=f"immunization-{idx}",
            )
            content.text = immunization.vaccine_name

            # Administration date
            td_date = etree.SubElement(tr, f"{{{NS}}}td")
            td_date.text = immunization.administration_date.strftime("%Y-%m-%d")

            # Status
            td_status = etree.SubElement(tr, f"{{{NS}}}td")
            td_status.text = immunization.status.capitalize()

            # Lot number
            td_lot = etree.SubElement(tr, f"{{{NS}}}td")
            if immunization.lot_number:
                td_lot.text = immunization.lot_number
            else:
                td_lot.text = "Not recorded"

            # Manufacturer
            td_manufacturer = etree.SubElement(tr, f"{{{NS}}}td")
            if immunization.manufacturer:
                td_manufacturer.text = immunization.manufacturer
            else:
                td_manufacturer.text = "Not recorded"

    def _add_entry(self, section: etree._Element, immunization: ImmunizationProtocol) -> None:
        """
        Add entry element with Immunization Activity.

        Args:
            section: section element
            immunization: Immunization data
        """
        # Create entry element
        entry = etree.SubElement(section, f"{{{NS}}}entry", typeCode="DRIV")

        # Create and add Immunization Activity
        imm_builder = ImmunizationActivity(immunization, version=self.version)
        entry.append(imm_builder.to_element())

Functions

__init__(immunizations, title='Immunizations', version=CDAVersion.R2_1, **kwargs)

Initialize ImmunizationsSection builder.

Parameters:

Name Type Description Default
immunizations Sequence[ImmunizationProtocol]

List of immunizations satisfying ImmunizationProtocol

required
title str

Section title (default: "Immunizations")

'Immunizations'
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/immunizations.py
def __init__(
    self,
    immunizations: Sequence[ImmunizationProtocol],
    title: str = "Immunizations",
    version: CDAVersion = CDAVersion.R2_1,
    **kwargs,
):
    """
    Initialize ImmunizationsSection builder.

    Args:
        immunizations: List of immunizations satisfying ImmunizationProtocol
        title: Section title (default: "Immunizations")
        version: C-CDA version (R2.1 or R2.0)
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.immunizations = immunizations
    self.title = title

build()

Build Immunizations Section XML element.

Returns:

Type Description
Element

lxml Element for section

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

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add template IDs
    self.add_template_ids(section)

    # Add section code (11369-6 = History of Immunization Narrative)
    code_elem = Code(
        code="11369-6",
        system="LOINC",
        display_name="History of Immunization Narrative",
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # Add title
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (HTML table)
    self._add_narrative(section)

    # Add entries with Immunization Activities
    for immunization in self.immunizations:
        self._add_entry(section, immunization)

    return section

InstructionsSection

InstructionsSection

Bases: CDAElement

Builder for C-CDA Instructions Section (V2).

This section records instructions given to a patient. It can be used to list patient decision aids, education materials, and other instructions provided to the patient or their caregivers.

Template ID: 2.16.840.1.113883.10.20.22.2.45 Release: 2014-06-09

Supports both R2.1 and R2.0 versions.

Conformance Rules: - MAY contain @nullFlavor="NI" (CONF:1098-32835) - SHALL contain templateId with root="2.16.840.1.113883.10.20.22.2.45" (CONF:1098-10112, CONF:1098-31384) - SHALL contain templateId extension="2014-06-09" (CONF:1098-32599) - SHALL contain code="69730-0" from LOINC (CONF:1098-15375, CONF:1098-15376, CONF:1098-32148) - SHALL contain title (CONF:1098-10114) - SHALL contain text (CONF:1098-10115) - SHALL contain at least one [1..*] entry with Instruction (V2) if @nullFlavor not present (CONF:1098-10116, CONF:1098-31398)

Common Uses: - Patient education materials - Decision aids - Care instructions - Discharge instructions - Medication instructions

Source code in ccdakit/builders/sections/instructions.py
class InstructionsSection(CDAElement):
    """
    Builder for C-CDA Instructions Section (V2).

    This section records instructions given to a patient. It can be used to list
    patient decision aids, education materials, and other instructions provided
    to the patient or their caregivers.

    Template ID: 2.16.840.1.113883.10.20.22.2.45
    Release: 2014-06-09

    Supports both R2.1 and R2.0 versions.

    Conformance Rules:
    - MAY contain @nullFlavor="NI" (CONF:1098-32835)
    - SHALL contain templateId with root="2.16.840.1.113883.10.20.22.2.45" (CONF:1098-10112, CONF:1098-31384)
    - SHALL contain templateId extension="2014-06-09" (CONF:1098-32599)
    - SHALL contain code="69730-0" from LOINC (CONF:1098-15375, CONF:1098-15376, CONF:1098-32148)
    - SHALL contain title (CONF:1098-10114)
    - SHALL contain text (CONF:1098-10115)
    - SHALL contain at least one [1..*] entry with Instruction (V2) if @nullFlavor not present (CONF:1098-10116, CONF:1098-31398)

    Common Uses:
    - Patient education materials
    - Decision aids
    - Care instructions
    - Discharge instructions
    - Medication instructions
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.45",
                extension="2014-06-09",
                description="Instructions Section (V2)",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.45",
                extension="2014-06-09",
                description="Instructions Section (V2)",
            ),
        ],
    }

    def __init__(
        self,
        instructions: Optional[Sequence[InstructionProtocol]] = None,
        title: str = "Instructions",
        version: CDAVersion = CDAVersion.R2_1,
        null_flavor: str = None,
        **kwargs,
    ):
        """
        Initialize InstructionsSection builder.

        Args:
            instructions: List of instructions satisfying InstructionProtocol
            title: Section title (default: "Instructions")
            version: C-CDA version (R2.1 or R2.0)
            null_flavor: Optional null flavor (e.g., "NI" for no information)
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.instructions = instructions or []
        self.title = title
        self.null_flavor = null_flavor

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

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add null flavor if specified (CONF:1098-32835)
        if self.null_flavor:
            section.set("nullFlavor", self.null_flavor)

        # Add template IDs (CONF:1098-10112, CONF:1098-31384, CONF:1098-32599)
        self.add_template_ids(section)

        # Add section code (CONF:1098-15375, CONF:1098-15376, CONF:1098-32148)
        # 69730-0 = Instructions (LOINC)
        code_elem = Code(
            code="69730-0",
            system="LOINC",
            display_name="Instructions",
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # Add title (CONF:1098-10114)
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (CONF:1098-10115)
        self._add_narrative(section)

        # Add entries with Instruction (V2) (CONF:1098-10116, CONF:1098-31398)
        # SHALL contain at least one entry if @nullFlavor is not present
        for instruction in self.instructions:
            self._add_instruction_entry(section, instruction)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element with HTML table.

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        if not self.instructions:
            # No instructions - add "No instructions documented" paragraph
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            paragraph.text = "No instructions documented"
            return

        # Create table for instructions
        table = etree.SubElement(text, f"{{{NS}}}table", border="1", width="100%")

        # Table header
        thead = etree.SubElement(table, f"{{{NS}}}thead")
        tr = etree.SubElement(thead, f"{{{NS}}}tr")

        headers = ["Instruction Type", "Details"]
        for header_text in headers:
            th = etree.SubElement(tr, f"{{{NS}}}th")
            th.text = header_text

        # Table body
        tbody = etree.SubElement(table, f"{{{NS}}}tbody")

        for idx, instruction in enumerate(self.instructions, start=1):
            tr = etree.SubElement(tbody, f"{{{NS}}}tr")

            # Instruction Type (code display name or default)
            td_type = etree.SubElement(tr, f"{{{NS}}}td")
            if hasattr(instruction, "display_name") and instruction.display_name:
                td_type.text = instruction.display_name
            elif hasattr(instruction, "code") and instruction.code:
                td_type.text = f"Instruction ({instruction.code})"
            else:
                td_type.text = "Instruction"

            # Details (with ID reference)
            td_details = etree.SubElement(tr, f"{{{NS}}}td")
            content = etree.SubElement(
                td_details,
                f"{{{NS}}}content",
                ID=f"instruction-{idx}",
            )
            # Support both 'text' and 'instruction_text' properties for backward compatibility
            if hasattr(instruction, "text"):
                content.text = instruction.text
            elif hasattr(instruction, "instruction_text"):
                content.text = instruction.instruction_text
            else:
                content.text = ""

    def _add_instruction_entry(
        self,
        section: etree._Element,
        instruction: InstructionProtocol,
    ) -> None:
        """
        Add entry element with Instruction (V2).

        Args:
            section: section element
            instruction: Instruction data
        """
        # Create entry element (SHALL contain - CONF:1098-10116)
        entry = etree.SubElement(section, f"{{{NS}}}entry")

        # Create and add Instruction (V2) (CONF:1098-31398)
        instruction_builder = Instruction(instruction, version=self.version)
        entry.append(instruction_builder.to_element())

Functions

__init__(instructions=None, title='Instructions', version=CDAVersion.R2_1, null_flavor=None, **kwargs)

Initialize InstructionsSection builder.

Parameters:

Name Type Description Default
instructions Optional[Sequence[InstructionProtocol]]

List of instructions satisfying InstructionProtocol

None
title str

Section title (default: "Instructions")

'Instructions'
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
null_flavor str

Optional null flavor (e.g., "NI" for no information)

None
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/instructions.py
def __init__(
    self,
    instructions: Optional[Sequence[InstructionProtocol]] = None,
    title: str = "Instructions",
    version: CDAVersion = CDAVersion.R2_1,
    null_flavor: str = None,
    **kwargs,
):
    """
    Initialize InstructionsSection builder.

    Args:
        instructions: List of instructions satisfying InstructionProtocol
        title: Section title (default: "Instructions")
        version: C-CDA version (R2.1 or R2.0)
        null_flavor: Optional null flavor (e.g., "NI" for no information)
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.instructions = instructions or []
    self.title = title
    self.null_flavor = null_flavor

build()

Build Instructions Section XML element.

Returns:

Type Description
Element

lxml Element for section

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

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add null flavor if specified (CONF:1098-32835)
    if self.null_flavor:
        section.set("nullFlavor", self.null_flavor)

    # Add template IDs (CONF:1098-10112, CONF:1098-31384, CONF:1098-32599)
    self.add_template_ids(section)

    # Add section code (CONF:1098-15375, CONF:1098-15376, CONF:1098-32148)
    # 69730-0 = Instructions (LOINC)
    code_elem = Code(
        code="69730-0",
        system="LOINC",
        display_name="Instructions",
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # Add title (CONF:1098-10114)
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (CONF:1098-10115)
    self._add_narrative(section)

    # Add entries with Instruction (V2) (CONF:1098-10116, CONF:1098-31398)
    # SHALL contain at least one entry if @nullFlavor is not present
    for instruction in self.instructions:
        self._add_instruction_entry(section, instruction)

    return section

InterventionsSection

InterventionsSection

Bases: CDAElement

Builder for C-CDA Interventions Section (V3).

This section represents interventions - actions taken to maximize the prospects of achieving the goals of care for the patient, including removal of barriers to success. Interventions can be planned, ordered, historical, etc.

Interventions include actions that may be ongoing (e.g., maintenance medications, monitoring health status). Instructions are nested within interventions and may include self-care instructions.

Template ID: 2.16.840.1.113883.10.20.21.2.3 Release: 2015-08-01

Supports both R2.1 and R2.0 versions.

Conformance Rules: - SHALL contain templateId with root="2.16.840.1.113883.10.20.21.2.3" (CONF:1198-8680, CONF:1198-10461) - SHALL contain templateId extension="2015-08-01" (CONF:1198-32559) - SHALL contain code="62387-6" from LOINC (CONF:1198-15377, CONF:1198-15378, CONF:1198-30864) - SHALL contain title (CONF:1198-8682) - SHALL contain text (CONF:1198-8683) - SHOULD contain zero or more [0..] entry with Intervention Act (CONF:1198-30996, CONF:1198-30997) - SHOULD contain zero or more [0..] entry with Planned Intervention Act (CONF:1198-32730, CONF:1198-32731)

Source code in ccdakit/builders/sections/interventions.py
class InterventionsSection(CDAElement):
    """
    Builder for C-CDA Interventions Section (V3).

    This section represents interventions - actions taken to maximize the prospects
    of achieving the goals of care for the patient, including removal of barriers
    to success. Interventions can be planned, ordered, historical, etc.

    Interventions include actions that may be ongoing (e.g., maintenance medications,
    monitoring health status). Instructions are nested within interventions and may
    include self-care instructions.

    Template ID: 2.16.840.1.113883.10.20.21.2.3
    Release: 2015-08-01

    Supports both R2.1 and R2.0 versions.

    Conformance Rules:
    - SHALL contain templateId with root="2.16.840.1.113883.10.20.21.2.3" (CONF:1198-8680, CONF:1198-10461)
    - SHALL contain templateId extension="2015-08-01" (CONF:1198-32559)
    - SHALL contain code="62387-6" from LOINC (CONF:1198-15377, CONF:1198-15378, CONF:1198-30864)
    - SHALL contain title (CONF:1198-8682)
    - SHALL contain text (CONF:1198-8683)
    - SHOULD contain zero or more [0..*] entry with Intervention Act (CONF:1198-30996, CONF:1198-30997)
    - SHOULD contain zero or more [0..*] entry with Planned Intervention Act (CONF:1198-32730, CONF:1198-32731)
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.21.2.3",
                extension="2015-08-01",
                description="Interventions Section (V3)",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.21.2.3",
                extension="2015-08-01",
                description="Interventions Section (V3)",
            ),
        ],
    }

    def __init__(
        self,
        interventions: Optional[Sequence[InterventionProtocol]] = None,
        planned_interventions: Optional[Sequence[PlannedInterventionProtocol]] = None,
        title: str = "Interventions",
        version: CDAVersion = CDAVersion.R2_1,
        null_flavor: str = None,
        **kwargs,
    ):
        """
        Initialize InterventionsSection builder.

        Args:
            interventions: List of completed interventions satisfying InterventionProtocol
            planned_interventions: List of planned interventions satisfying PlannedInterventionProtocol
            title: Section title (default: "Interventions")
            version: C-CDA version (R2.1 or R2.0)
            null_flavor: Optional null flavor (e.g., "NI" for no information)
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.interventions = interventions or []
        self.planned_interventions = planned_interventions or []
        self.title = title
        self.null_flavor = null_flavor

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

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add null flavor if specified
        if self.null_flavor:
            section.set("nullFlavor", self.null_flavor)

        # Add template IDs (CONF:1198-8680, CONF:1198-10461, CONF:1198-32559)
        self.add_template_ids(section)

        # Add section code (CONF:1198-15377, CONF:1198-15378, CONF:1198-30864)
        # 62387-6 = Interventions Provided (LOINC)
        code_elem = Code(
            code="62387-6",
            system="LOINC",
            display_name="Interventions Provided",
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # Add title (CONF:1198-8682)
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (CONF:1198-8683)
        self._add_narrative(section)

        # Add entries with Intervention Acts (CONF:1198-30996, CONF:1198-30997)
        for intervention in self.interventions:
            self._add_intervention_entry(section, intervention)

        # Add entries with Planned Intervention Acts (CONF:1198-32730, CONF:1198-32731)
        for planned in self.planned_interventions:
            self._add_planned_intervention_entry(section, planned)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element with HTML table.

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        # Collect all interventions
        all_interventions = []
        for intervention in self.interventions:
            all_interventions.append(("Completed", intervention))
        for planned in self.planned_interventions:
            all_interventions.append(("Planned", planned))

        if not all_interventions:
            # No interventions - add "No interventions documented" paragraph
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            paragraph.text = "No interventions documented"
            return

        # Create table for interventions
        table = etree.SubElement(text, f"{{{NS}}}table", border="1", width="100%")

        # Table header
        thead = etree.SubElement(table, f"{{{NS}}}thead")
        tr = etree.SubElement(thead, f"{{{NS}}}tr")

        headers = ["Type", "Description", "Status", "Date", "Goal Reference"]
        for header_text in headers:
            th = etree.SubElement(tr, f"{{{NS}}}th")
            th.text = header_text

        # Table body
        tbody = etree.SubElement(table, f"{{{NS}}}tbody")

        for idx, (intervention_type, intervention) in enumerate(all_interventions, start=1):
            tr = etree.SubElement(tbody, f"{{{NS}}}tr")

            # Type (Completed/Planned)
            td_type = etree.SubElement(tr, f"{{{NS}}}td")
            td_type.text = intervention_type

            # Description (with ID reference)
            td_desc = etree.SubElement(tr, f"{{{NS}}}td")
            content = etree.SubElement(
                td_desc,
                f"{{{NS}}}content",
                ID=f"intervention-{idx}",
            )
            content.text = intervention.description

            # Status
            td_status = etree.SubElement(tr, f"{{{NS}}}td")
            if hasattr(intervention, "status"):
                td_status.text = self._format_status(intervention.status)
            else:
                td_status.text = "Active" if intervention_type == "Planned" else "Completed"

            # Date
            td_date = etree.SubElement(tr, f"{{{NS}}}td")
            if hasattr(intervention, "effective_time") and intervention.effective_time:
                eff_time = intervention.effective_time
                if hasattr(eff_time, "strftime"):
                    td_date.text = eff_time.strftime("%Y-%m-%d")
                else:
                    td_date.text = str(eff_time)
            else:
                td_date.text = "Not specified"

            # Goal Reference
            td_goal = etree.SubElement(tr, f"{{{NS}}}td")
            if hasattr(intervention, "goal_reference_id") and intervention.goal_reference_id:
                td_goal.text = f"Goal: {intervention.goal_reference_id}"
            else:
                td_goal.text = "Not specified"

    def _add_intervention_entry(
        self,
        section: etree._Element,
        intervention: InterventionProtocol,
    ) -> None:
        """
        Add entry element with Intervention Act.

        Args:
            section: section element
            intervention: Intervention data
        """
        # Create entry element (SHOULD contain - CONF:1198-30996)
        entry = etree.SubElement(section, f"{{{NS}}}entry")

        # Create and add Intervention Act (CONF:1198-30997)
        act_builder = InterventionAct(intervention, version=self.version)
        entry.append(act_builder.to_element())

    def _add_planned_intervention_entry(
        self,
        section: etree._Element,
        planned: PlannedInterventionProtocol,
    ) -> None:
        """
        Add entry element with Planned Intervention Act.

        Args:
            section: section element
            planned: Planned intervention data
        """
        # Create entry element (SHOULD contain - CONF:1198-32730)
        entry = etree.SubElement(section, f"{{{NS}}}entry")

        # Create and add Planned Intervention Act (CONF:1198-32731)
        act_builder = PlannedInterventionAct(planned, version=self.version)
        entry.append(act_builder.to_element())

    def _format_status(self, status: str) -> str:
        """
        Format status for display in narrative.

        Args:
            status: Raw status string

        Returns:
            Formatted status string
        """
        # Capitalize and replace hyphens with spaces
        formatted = status.replace("-", " ").replace("_", " ")
        return formatted.title()

Functions

__init__(interventions=None, planned_interventions=None, title='Interventions', version=CDAVersion.R2_1, null_flavor=None, **kwargs)

Initialize InterventionsSection builder.

Parameters:

Name Type Description Default
interventions Optional[Sequence[InterventionProtocol]]

List of completed interventions satisfying InterventionProtocol

None
planned_interventions Optional[Sequence[PlannedInterventionProtocol]]

List of planned interventions satisfying PlannedInterventionProtocol

None
title str

Section title (default: "Interventions")

'Interventions'
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
null_flavor str

Optional null flavor (e.g., "NI" for no information)

None
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/interventions.py
def __init__(
    self,
    interventions: Optional[Sequence[InterventionProtocol]] = None,
    planned_interventions: Optional[Sequence[PlannedInterventionProtocol]] = None,
    title: str = "Interventions",
    version: CDAVersion = CDAVersion.R2_1,
    null_flavor: str = None,
    **kwargs,
):
    """
    Initialize InterventionsSection builder.

    Args:
        interventions: List of completed interventions satisfying InterventionProtocol
        planned_interventions: List of planned interventions satisfying PlannedInterventionProtocol
        title: Section title (default: "Interventions")
        version: C-CDA version (R2.1 or R2.0)
        null_flavor: Optional null flavor (e.g., "NI" for no information)
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.interventions = interventions or []
    self.planned_interventions = planned_interventions or []
    self.title = title
    self.null_flavor = null_flavor

build()

Build Interventions Section XML element.

Returns:

Type Description
Element

lxml Element for section

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

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add null flavor if specified
    if self.null_flavor:
        section.set("nullFlavor", self.null_flavor)

    # Add template IDs (CONF:1198-8680, CONF:1198-10461, CONF:1198-32559)
    self.add_template_ids(section)

    # Add section code (CONF:1198-15377, CONF:1198-15378, CONF:1198-30864)
    # 62387-6 = Interventions Provided (LOINC)
    code_elem = Code(
        code="62387-6",
        system="LOINC",
        display_name="Interventions Provided",
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # Add title (CONF:1198-8682)
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (CONF:1198-8683)
    self._add_narrative(section)

    # Add entries with Intervention Acts (CONF:1198-30996, CONF:1198-30997)
    for intervention in self.interventions:
        self._add_intervention_entry(section, intervention)

    # Add entries with Planned Intervention Acts (CONF:1198-32730, CONF:1198-32731)
    for planned in self.planned_interventions:
        self._add_planned_intervention_entry(section, planned)

    return section

MedicalEquipmentSection

MedicalEquipmentSection

Bases: CDAElement

Builder for C-CDA Medical Equipment Section.

Defines a patient's implanted and external health and medical devices and equipment. Lists any pertinent durable medical equipment (DME) used to help maintain the patient's health status.

Includes narrative (HTML table) and structured entries. Supports both R2.1 and R2.0 versions (both use 2014-06-09 extension).

Source code in ccdakit/builders/sections/medical_equipment.py
class MedicalEquipmentSection(CDAElement):
    """
    Builder for C-CDA Medical Equipment Section.

    Defines a patient's implanted and external health and medical devices and equipment.
    Lists any pertinent durable medical equipment (DME) used to help maintain the
    patient's health status.

    Includes narrative (HTML table) and structured entries.
    Supports both R2.1 and R2.0 versions (both use 2014-06-09 extension).
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.23",
                extension="2014-06-09",
                description="Medical Equipment Section V2",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.23",
                extension="2014-06-09",
                description="Medical Equipment Section V2",
            ),
        ],
    }

    def __init__(
        self,
        equipment_list: Sequence[MedicalEquipmentProtocol],
        title: str = "Medical Equipment",
        use_organizer: bool = False,
        organizer_start_date: Optional[object] = None,
        organizer_end_date: Optional[object] = None,
        version: CDAVersion = CDAVersion.R2_1,
        **kwargs,
    ):
        """
        Initialize MedicalEquipmentSection builder.

        Args:
            equipment_list: List of medical equipment items satisfying MedicalEquipmentProtocol
            title: Section title (default: "Medical Equipment")
            use_organizer: If True, wrap equipment in Medical Equipment Organizer
            organizer_start_date: Start date if using organizer
            organizer_end_date: End date if using organizer
            version: C-CDA version (R2.1 or R2.0)
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.equipment_list = equipment_list
        self.title = title
        self.use_organizer = use_organizer
        self.organizer_start_date = organizer_start_date
        self.organizer_end_date = organizer_end_date

    def build(self) -> etree.Element:
        """
        Build Medical Equipment Section XML element.

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add template IDs (CONF:1098-7944, CONF:1098-10404, CONF:1098-32523)
        self.add_template_ids(section)

        # Add section code (CONF:1098-15381, CONF:1098-15382, CONF:1098-30828)
        code_elem = Code(
            code="46264-8",  # CONF:1098-15382
            system="LOINC",  # CONF:1098-30828
            display_name="Medical Equipment",
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # Add title (CONF:1098-7946)
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (CONF:1098-7947)
        self._add_narrative(section)

        # Add entries
        if self.use_organizer and self.equipment_list:
            # Add Medical Equipment Organizer (CONF:1098-7948, CONF:1098-30351)
            self._add_organizer_entry(section)
        else:
            # Add individual Non-Medicinal Supply Activity entries (CONF:1098-31125, CONF:1098-31861)
            for equipment in self.equipment_list:
                self._add_supply_entry(section, equipment)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element with HTML table.

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        if not self.equipment_list:
            # No equipment - add "No medical equipment recorded" paragraph
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            paragraph.text = "No medical equipment recorded"
            return

        # Create table for equipment
        table = etree.SubElement(text, f"{{{NS}}}table", border="1", width="100%")

        # Table header
        thead = etree.SubElement(table, f"{{{NS}}}thead")
        tr = etree.SubElement(thead, f"{{{NS}}}tr")

        headers = [
            "Equipment",
            "Code",
            "Date Supplied",
            "Date End",
            "Quantity",
            "Status",
            "Manufacturer",
            "Model/Serial",
        ]
        for header_text in headers:
            th = etree.SubElement(tr, f"{{{NS}}}th")
            th.text = header_text

        # Table body
        tbody = etree.SubElement(table, f"{{{NS}}}tbody")

        for idx, equipment in enumerate(self.equipment_list, start=1):
            tr = etree.SubElement(tbody, f"{{{NS}}}tr")

            # Equipment name (with ID reference)
            td_name = etree.SubElement(tr, f"{{{NS}}}td")
            content = etree.SubElement(
                td_name,
                f"{{{NS}}}content",
                ID=f"equipment-{idx}",
            )
            content.text = equipment.name

            # Code
            td_code = etree.SubElement(tr, f"{{{NS}}}td")
            if equipment.code and equipment.code_system:
                td_code.text = f"{equipment.code} ({equipment.code_system})"
            else:
                td_code.text = "-"

            # Date supplied
            td_date_start = etree.SubElement(tr, f"{{{NS}}}td")
            if equipment.date_supplied:
                if hasattr(equipment.date_supplied, "hour"):
                    td_date_start.text = equipment.date_supplied.strftime("%Y-%m-%d %H:%M")
                else:
                    td_date_start.text = equipment.date_supplied.strftime("%Y-%m-%d")
            else:
                td_date_start.text = "-"

            # Date end
            td_date_end = etree.SubElement(tr, f"{{{NS}}}td")
            if equipment.date_end:
                if hasattr(equipment.date_end, "hour"):
                    td_date_end.text = equipment.date_end.strftime("%Y-%m-%d %H:%M")
                else:
                    td_date_end.text = equipment.date_end.strftime("%Y-%m-%d")
            else:
                td_date_end.text = "-"

            # Quantity
            td_quantity = etree.SubElement(tr, f"{{{NS}}}td")
            if equipment.quantity is not None:
                td_quantity.text = str(equipment.quantity)
            else:
                td_quantity.text = "-"

            # Status
            td_status = etree.SubElement(tr, f"{{{NS}}}td")
            td_status.text = equipment.status.capitalize()

            # Manufacturer
            td_manufacturer = etree.SubElement(tr, f"{{{NS}}}td")
            if equipment.manufacturer:
                td_manufacturer.text = equipment.manufacturer
            else:
                td_manufacturer.text = "-"

            # Model/Serial
            td_model = etree.SubElement(tr, f"{{{NS}}}td")
            parts = []
            if equipment.model_number:
                parts.append(f"Model: {equipment.model_number}")
            if equipment.serial_number:
                parts.append(f"S/N: {equipment.serial_number}")
            if parts:
                td_model.text = ", ".join(parts)
            else:
                td_model.text = "-"

    def _add_supply_entry(
        self, section: etree._Element, equipment: MedicalEquipmentProtocol
    ) -> None:
        """
        Add entry element with Non-Medicinal Supply Activity (CONF:1098-31125, CONF:1098-31861).

        Args:
            section: section element
            equipment: Medical equipment data
        """
        # Create entry element
        entry = etree.SubElement(section, f"{{{NS}}}entry")

        # Create and add Non-Medicinal Supply Activity
        supply_builder = NonMedicinalSupplyActivity(equipment, version=self.version)
        entry.append(supply_builder.to_element())

    def _add_organizer_entry(self, section: etree._Element) -> None:
        """
        Add entry element with Medical Equipment Organizer (CONF:1098-7948, CONF:1098-30351).

        Args:
            section: section element
        """
        # Create entry element
        entry = etree.SubElement(section, f"{{{NS}}}entry")

        # Create and add Medical Equipment Organizer
        organizer_builder = MedicalEquipmentOrganizer(
            equipment_list=self.equipment_list,
            status="completed",
            date_start=self.organizer_start_date,
            date_end=self.organizer_end_date,
            version=self.version,
        )
        entry.append(organizer_builder.to_element())

Functions

__init__(equipment_list, title='Medical Equipment', use_organizer=False, organizer_start_date=None, organizer_end_date=None, version=CDAVersion.R2_1, **kwargs)

Initialize MedicalEquipmentSection builder.

Parameters:

Name Type Description Default
equipment_list Sequence[MedicalEquipmentProtocol]

List of medical equipment items satisfying MedicalEquipmentProtocol

required
title str

Section title (default: "Medical Equipment")

'Medical Equipment'
use_organizer bool

If True, wrap equipment in Medical Equipment Organizer

False
organizer_start_date Optional[object]

Start date if using organizer

None
organizer_end_date Optional[object]

End date if using organizer

None
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/medical_equipment.py
def __init__(
    self,
    equipment_list: Sequence[MedicalEquipmentProtocol],
    title: str = "Medical Equipment",
    use_organizer: bool = False,
    organizer_start_date: Optional[object] = None,
    organizer_end_date: Optional[object] = None,
    version: CDAVersion = CDAVersion.R2_1,
    **kwargs,
):
    """
    Initialize MedicalEquipmentSection builder.

    Args:
        equipment_list: List of medical equipment items satisfying MedicalEquipmentProtocol
        title: Section title (default: "Medical Equipment")
        use_organizer: If True, wrap equipment in Medical Equipment Organizer
        organizer_start_date: Start date if using organizer
        organizer_end_date: End date if using organizer
        version: C-CDA version (R2.1 or R2.0)
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.equipment_list = equipment_list
    self.title = title
    self.use_organizer = use_organizer
    self.organizer_start_date = organizer_start_date
    self.organizer_end_date = organizer_end_date

build()

Build Medical Equipment Section XML element.

Returns:

Type Description
Element

lxml Element for section

Source code in ccdakit/builders/sections/medical_equipment.py
def build(self) -> etree.Element:
    """
    Build Medical Equipment Section XML element.

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add template IDs (CONF:1098-7944, CONF:1098-10404, CONF:1098-32523)
    self.add_template_ids(section)

    # Add section code (CONF:1098-15381, CONF:1098-15382, CONF:1098-30828)
    code_elem = Code(
        code="46264-8",  # CONF:1098-15382
        system="LOINC",  # CONF:1098-30828
        display_name="Medical Equipment",
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # Add title (CONF:1098-7946)
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (CONF:1098-7947)
    self._add_narrative(section)

    # Add entries
    if self.use_organizer and self.equipment_list:
        # Add Medical Equipment Organizer (CONF:1098-7948, CONF:1098-30351)
        self._add_organizer_entry(section)
    else:
        # Add individual Non-Medicinal Supply Activity entries (CONF:1098-31125, CONF:1098-31861)
        for equipment in self.equipment_list:
            self._add_supply_entry(section, equipment)

    return section

MedicationsAdministeredSection

MedicationsAdministeredSection

Bases: CDAElement

Builder for C-CDA Medications Administered Section (V2).

This section documents medications and fluids administered during a procedure, encounter, or other clinical activity, excluding anesthetic medications (which should be documented in the Anesthesia Section).

Template ID: 2.16.840.1.113883.10.20.22.2.38 (2014-06-09) LOINC Code: 29549-3 (Medications Administered)

Typically used in: - Procedure Notes - Emergency Department visits - Inpatient encounters - Surgical procedures

Note: Anesthesia medications should use the Anesthesia Section (2.16.840.1.113883.10.20.22.2.25) instead.

Source code in ccdakit/builders/sections/medications_administered.py
class MedicationsAdministeredSection(CDAElement):
    """
    Builder for C-CDA Medications Administered Section (V2).

    This section documents medications and fluids administered during a procedure,
    encounter, or other clinical activity, excluding anesthetic medications (which
    should be documented in the Anesthesia Section).

    Template ID: 2.16.840.1.113883.10.20.22.2.38 (2014-06-09)
    LOINC Code: 29549-3 (Medications Administered)

    Typically used in:
    - Procedure Notes
    - Emergency Department visits
    - Inpatient encounters
    - Surgical procedures

    Note: Anesthesia medications should use the Anesthesia Section
    (2.16.840.1.113883.10.20.22.2.25) instead.
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.38",
                extension="2014-06-09",
                description="Medications Administered Section (V2) R2.1",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.38",
                extension="2014-06-09",
                description="Medications Administered Section (V2) R2.0",
            ),
        ],
    }

    def __init__(
        self,
        medications: Sequence[MedicationAdministeredProtocol],
        title: str = "Medications Administered",
        version: CDAVersion = CDAVersion.R2_1,
        null_flavor: Optional[str] = None,
        **kwargs,
    ):
        """
        Initialize MedicationsAdministeredSection builder.

        Args:
            medications: List of medications satisfying MedicationAdministeredProtocol
            title: Section title (default: "Medications Administered")
            version: C-CDA version (R2.1 or R2.0)
            null_flavor: Null flavor code if section has no information (e.g., "NI")
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.medications = medications
        self.title = title
        self.null_flavor = null_flavor

    def build(self) -> etree.Element:
        """
        Build Medications Administered Section XML element.

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add null flavor if specified
        if self.null_flavor:
            section.set("nullFlavor", self.null_flavor)

        # Add template IDs (CONF:1098-8152, CONF:1098-10405, CONF:1098-32525)
        self.add_template_ids(section)

        # Add section code (CONF:1098-15383, CONF:1098-15384, CONF:1098-30829)
        code_elem = Code(
            code="29549-3",
            system="LOINC",
            display_name="Medications Administered",
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # Add title (CONF:1098-8154)
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (HTML table) (CONF:1098-8155)
        self._add_narrative(section)

        # Add entries with Medication Activities (CONF:1098-8156, CONF:1098-15499)
        if not self.null_flavor:
            for medication in self.medications:
                self._add_entry(section, medication)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element with HTML table.

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        if self.null_flavor:
            # No information available
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            paragraph.text = "No information available about medications administered"
            return

        if not self.medications:
            # No medications administered
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            paragraph.text = "No medications administered"
            return

        # Create table for medications
        table = etree.SubElement(text, f"{{{NS}}}table", border="1", width="100%")

        # Table header
        thead = etree.SubElement(table, f"{{{NS}}}thead")
        tr = etree.SubElement(thead, f"{{{NS}}}tr")

        headers = [
            "Medication",
            "Dose",
            "Route",
            "Administration Time",
            "Site",
            "Rate",
            "Performer",
            "Status",
        ]
        for header_text in headers:
            th = etree.SubElement(tr, f"{{{NS}}}th")
            th.text = header_text

        # Table body
        tbody = etree.SubElement(table, f"{{{NS}}}tbody")

        for idx, medication in enumerate(self.medications, start=1):
            tr = etree.SubElement(tbody, f"{{{NS}}}tr")

            # Medication name (with ID reference)
            td_name = etree.SubElement(tr, f"{{{NS}}}td")
            content = etree.SubElement(
                td_name,
                f"{{{NS}}}content",
                ID=f"medication-administered-{idx}",
            )
            content.text = medication.name

            # Dose
            td_dose = etree.SubElement(tr, f"{{{NS}}}td")
            td_dose.text = medication.dose

            # Route
            td_route = etree.SubElement(tr, f"{{{NS}}}td")
            td_route.text = medication.route.capitalize()

            # Administration time
            td_time = etree.SubElement(tr, f"{{{NS}}}td")
            if medication.administration_end_time:
                # Show time range for infusions
                td_time.text = (
                    f"{medication.administration_time.strftime('%Y-%m-%d %H:%M')} - "
                    f"{medication.administration_end_time.strftime('%Y-%m-%d %H:%M')}"
                )
            else:
                td_time.text = medication.administration_time.strftime("%Y-%m-%d %H:%M")

            # Site (optional)
            td_site = etree.SubElement(tr, f"{{{NS}}}td")
            td_site.text = medication.site if medication.site else "-"

            # Rate (optional)
            td_rate = etree.SubElement(tr, f"{{{NS}}}td")
            td_rate.text = medication.rate if medication.rate else "-"

            # Performer (optional)
            td_performer = etree.SubElement(tr, f"{{{NS}}}td")
            td_performer.text = medication.performer if medication.performer else "-"

            # Status
            td_status = etree.SubElement(tr, f"{{{NS}}}td")
            td_status.text = medication.status.capitalize()

    def _add_entry(
        self, section: etree._Element, medication: MedicationAdministeredProtocol
    ) -> None:
        """
        Add entry element with Medication Activity.

        Args:
            section: section element
            medication: Medication administration data
        """
        # Create entry element
        entry = etree.SubElement(section, f"{{{NS}}}entry", typeCode="DRIV")

        # Create and add Medication Administered Activity
        med_builder = MedicationAdministeredActivity(medication, version=self.version)
        entry.append(med_builder.to_element())

Functions

__init__(medications, title='Medications Administered', version=CDAVersion.R2_1, null_flavor=None, **kwargs)

Initialize MedicationsAdministeredSection builder.

Parameters:

Name Type Description Default
medications Sequence[MedicationAdministeredProtocol]

List of medications satisfying MedicationAdministeredProtocol

required
title str

Section title (default: "Medications Administered")

'Medications Administered'
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
null_flavor Optional[str]

Null flavor code if section has no information (e.g., "NI")

None
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/medications_administered.py
def __init__(
    self,
    medications: Sequence[MedicationAdministeredProtocol],
    title: str = "Medications Administered",
    version: CDAVersion = CDAVersion.R2_1,
    null_flavor: Optional[str] = None,
    **kwargs,
):
    """
    Initialize MedicationsAdministeredSection builder.

    Args:
        medications: List of medications satisfying MedicationAdministeredProtocol
        title: Section title (default: "Medications Administered")
        version: C-CDA version (R2.1 or R2.0)
        null_flavor: Null flavor code if section has no information (e.g., "NI")
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.medications = medications
    self.title = title
    self.null_flavor = null_flavor

build()

Build Medications Administered Section XML element.

Returns:

Type Description
Element

lxml Element for section

Source code in ccdakit/builders/sections/medications_administered.py
def build(self) -> etree.Element:
    """
    Build Medications Administered Section XML element.

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add null flavor if specified
    if self.null_flavor:
        section.set("nullFlavor", self.null_flavor)

    # Add template IDs (CONF:1098-8152, CONF:1098-10405, CONF:1098-32525)
    self.add_template_ids(section)

    # Add section code (CONF:1098-15383, CONF:1098-15384, CONF:1098-30829)
    code_elem = Code(
        code="29549-3",
        system="LOINC",
        display_name="Medications Administered",
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # Add title (CONF:1098-8154)
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (HTML table) (CONF:1098-8155)
    self._add_narrative(section)

    # Add entries with Medication Activities (CONF:1098-8156, CONF:1098-15499)
    if not self.null_flavor:
        for medication in self.medications:
            self._add_entry(section, medication)

    return section

MedicationsSection

MedicationsSection

Bases: CDAElement

Builder for C-CDA Medications Section.

Includes narrative (HTML table) and structured entries. Supports both R2.1 (2015-08-01) and R2.0 (2014-06-09) versions.

Source code in ccdakit/builders/sections/medications.py
class MedicationsSection(CDAElement):
    """
    Builder for C-CDA Medications Section.

    Includes narrative (HTML table) and structured entries.
    Supports both R2.1 (2015-08-01) and R2.0 (2014-06-09) versions.
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.1.1",
                extension="2015-08-01",
                description="Medications Section (entries required) R2.1",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.1.1",
                extension="2014-06-09",
                description="Medications Section (entries required) R2.0",
            ),
        ],
    }

    def __init__(
        self,
        medications: Sequence[MedicationProtocol],
        title: str = "Medications",
        version: CDAVersion = CDAVersion.R2_1,
        **kwargs,
    ):
        """
        Initialize MedicationsSection builder.

        Args:
            medications: List of medications satisfying MedicationProtocol
            title: Section title (default: "Medications")
            version: C-CDA version (R2.1 or R2.0)
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.medications = medications
        self.title = title

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

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add template IDs
        self.add_template_ids(section)

        # Add section code (10160-0 = History of Medication use Narrative)
        code_elem = Code(
            code="10160-0",
            system="LOINC",
            display_name="History of Medication use Narrative",
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # Add title
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (HTML table)
        self._add_narrative(section)

        # Add entries with Medication Activities
        for medication in self.medications:
            self._add_entry(section, medication)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element with HTML table.

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        if not self.medications:
            # No medications - add "No known medications" paragraph
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            paragraph.text = "No known medications"
            return

        # Create table for medications
        table = etree.SubElement(text, f"{{{NS}}}table", border="1", width="100%")

        # Table header
        thead = etree.SubElement(table, f"{{{NS}}}thead")
        tr = etree.SubElement(thead, f"{{{NS}}}tr")

        headers = [
            "Medication",
            "Dosage",
            "Route",
            "Frequency",
            "Start Date",
            "End Date",
            "Status",
        ]
        for header_text in headers:
            th = etree.SubElement(tr, f"{{{NS}}}th")
            th.text = header_text

        # Table body
        tbody = etree.SubElement(table, f"{{{NS}}}tbody")

        for idx, medication in enumerate(self.medications, start=1):
            tr = etree.SubElement(tbody, f"{{{NS}}}tr")

            # Medication name (with ID reference)
            td_name = etree.SubElement(tr, f"{{{NS}}}td")
            content = etree.SubElement(
                td_name,
                f"{{{NS}}}content",
                ID=f"medication-{idx}",
            )
            content.text = medication.name

            # Dosage
            td_dosage = etree.SubElement(tr, f"{{{NS}}}td")
            td_dosage.text = medication.dosage

            # Route
            td_route = etree.SubElement(tr, f"{{{NS}}}td")
            td_route.text = medication.route.capitalize()

            # Frequency
            td_frequency = etree.SubElement(tr, f"{{{NS}}}td")
            td_frequency.text = medication.frequency

            # Start date
            td_start = etree.SubElement(tr, f"{{{NS}}}td")
            td_start.text = medication.start_date.strftime("%Y-%m-%d")

            # End date
            td_end = etree.SubElement(tr, f"{{{NS}}}td")
            if medication.end_date:
                td_end.text = medication.end_date.strftime("%Y-%m-%d")
            else:
                td_end.text = "Ongoing" if medication.status == "active" else "Unknown"

            # Status
            td_status = etree.SubElement(tr, f"{{{NS}}}td")
            td_status.text = medication.status.capitalize()

    def _add_entry(self, section: etree._Element, medication: MedicationProtocol) -> None:
        """
        Add entry element with Medication Activity.

        Args:
            section: section element
            medication: Medication data
        """
        # Create entry element
        entry = etree.SubElement(section, f"{{{NS}}}entry", typeCode="DRIV")

        # Create and add Medication Activity
        med_builder = MedicationActivity(medication, version=self.version)
        entry.append(med_builder.to_element())

Functions

__init__(medications, title='Medications', version=CDAVersion.R2_1, **kwargs)

Initialize MedicationsSection builder.

Parameters:

Name Type Description Default
medications Sequence[MedicationProtocol]

List of medications satisfying MedicationProtocol

required
title str

Section title (default: "Medications")

'Medications'
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/medications.py
def __init__(
    self,
    medications: Sequence[MedicationProtocol],
    title: str = "Medications",
    version: CDAVersion = CDAVersion.R2_1,
    **kwargs,
):
    """
    Initialize MedicationsSection builder.

    Args:
        medications: List of medications satisfying MedicationProtocol
        title: Section title (default: "Medications")
        version: C-CDA version (R2.1 or R2.0)
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.medications = medications
    self.title = title

build()

Build Medications Section XML element.

Returns:

Type Description
Element

lxml Element for section

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

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add template IDs
    self.add_template_ids(section)

    # Add section code (10160-0 = History of Medication use Narrative)
    code_elem = Code(
        code="10160-0",
        system="LOINC",
        display_name="History of Medication use Narrative",
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # Add title
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (HTML table)
    self._add_narrative(section)

    # Add entries with Medication Activities
    for medication in self.medications:
        self._add_entry(section, medication)

    return section

MentalStatusSection

MentalStatusSection

Bases: CDAElement

Builder for C-CDA Mental Status Section (V2).

Contains observations and evaluations related to a patient's psychological and mental competency including appearance, attitude, behavior, mood and affect, speech and language, thought process, thought content, perception, cognition, insight and judgment.

Template ID: 2.16.840.1.113883.10.20.22.2.56 Supports both R2.1 (2015-08-01) and R2.0 (2015-08-01) versions.

Includes narrative (HTML table) and structured entries.

Source code in ccdakit/builders/sections/mental_status.py
class MentalStatusSection(CDAElement):
    """
    Builder for C-CDA Mental Status Section (V2).

    Contains observations and evaluations related to a patient's psychological
    and mental competency including appearance, attitude, behavior, mood and affect,
    speech and language, thought process, thought content, perception, cognition,
    insight and judgment.

    Template ID: 2.16.840.1.113883.10.20.22.2.56
    Supports both R2.1 (2015-08-01) and R2.0 (2015-08-01) versions.

    Includes narrative (HTML table) and structured entries.
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.56",
                extension="2015-08-01",
                description="Mental Status Section (V2) R2.1",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.56",
                extension="2015-08-01",
                description="Mental Status Section (V2) R2.0",
            ),
        ],
    }

    def __init__(
        self,
        observations: Sequence[MentalStatusObservationProtocol] = None,
        organizers: Sequence[MentalStatusOrganizerProtocol] = None,
        title: str = "Mental Status",
        version: CDAVersion = CDAVersion.R2_1,
        **kwargs,
    ):
        """
        Initialize MentalStatusSection builder.

        Args:
            observations: List of mental status observations (optional)
            organizers: List of mental status organizers (optional)
            title: Section title (default: "Mental Status")
            version: C-CDA version (R2.1 or R2.0)
            **kwargs: Additional arguments passed to CDAElement

        Note:
            Either observations or organizers (or both) should be provided.
            If neither is provided, the section will show "No mental status observations recorded."
        """
        super().__init__(version=version, **kwargs)
        self.observations = observations or []
        self.organizers = organizers or []
        self.title = title

    def build(self) -> etree.Element:
        """
        Build Mental Status Section XML element.

        Returns:
            lxml Element for section

        CONF Rules Implemented:
            - CONF:1198-28293, CONF:1198-28294, CONF:1198-32793: templateId
            - CONF:1198-28295, CONF:1198-28296, CONF:1198-30826: code=10190-7 (LOINC)
            - CONF:1198-28297: title [1..1]
            - CONF:1198-28298: text [1..1]
            - CONF:1198-28301, CONF:1198-28302: entry with Mental Status Organizer [0..*]
            - CONF:1198-28305, CONF:1198-28306: entry with Mental Status Observation [0..*]
            - CONF:1198-28313, CONF:1198-28314: entry with Assessment Scale Observation [0..*]
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # CONF:1198-28293, CONF:1198-28294, CONF:1198-32793: Add template IDs
        self.add_template_ids(section)

        # CONF:1198-28295, CONF:1198-28296, CONF:1198-30826: Add section code
        code_elem = Code(
            code="10190-7",
            system="LOINC",
            display_name="Mental Status",
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # CONF:1198-28297: Add title
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # CONF:1198-28298: Add narrative text (HTML table)
        self._add_narrative(section)

        # CONF:1198-28301, CONF:1198-28302: Add entries with Mental Status Organizers
        for organizer in self.organizers:
            self._add_organizer_entry(section, organizer)

        # CONF:1198-28305, CONF:1198-28306: Add entries with Mental Status Observations
        for observation in self.observations:
            self._add_observation_entry(section, observation)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element with HTML table.

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        # Check if we have any data to display
        if not self.observations and not self.organizers:
            # No mental status data - add "No observations recorded" paragraph
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            paragraph.text = "No mental status observations recorded"
            return

        # Create table for mental status observations
        table = etree.SubElement(text, f"{{{NS}}}table", border="1", width="100%")

        # Table header
        thead = etree.SubElement(table, f"{{{NS}}}thead")
        tr = etree.SubElement(thead, f"{{{NS}}}tr")

        headers = ["Category", "Finding/Value", "Date", "Status"]
        for header_text in headers:
            th = etree.SubElement(tr, f"{{{NS}}}th")
            th.text = header_text

        # Table body
        tbody = etree.SubElement(table, f"{{{NS}}}tbody")

        # Add observations from organizers (grouped)
        idx = 1
        for organizer in self.organizers:
            for obs in organizer.observations:
                self._add_observation_row(tbody, obs, organizer.category, idx)
                idx += 1

        # Add standalone observations
        for obs in self.observations:
            category = obs.category if obs.category else "General"
            self._add_observation_row(tbody, obs, category, idx)
            idx += 1

    def _add_observation_row(
        self,
        tbody: etree._Element,
        obs: MentalStatusObservationProtocol,
        category: str,
        idx: int,
    ) -> None:
        """
        Add a single observation row to the narrative table.

        Args:
            tbody: table body element
            obs: observation data
            category: category name
            idx: row index for ID
        """
        tr = etree.SubElement(tbody, f"{{{NS}}}tr")

        # Category
        td_category = etree.SubElement(tr, f"{{{NS}}}td")
        td_category.text = category

        # Finding/Value (with ID reference)
        td_value = etree.SubElement(tr, f"{{{NS}}}td")
        content = etree.SubElement(
            td_value,
            f"{{{NS}}}content",
            ID=f"mental-status-{idx}",
        )
        content.text = obs.value

        # Date
        td_date = etree.SubElement(tr, f"{{{NS}}}td")
        obs_date = obs.observation_date
        if obs_date:
            td_date.text = obs_date.strftime("%Y-%m-%d")
        else:
            td_date.text = "Unknown"

        # Status
        td_status = etree.SubElement(tr, f"{{{NS}}}td")
        td_status.text = obs.status.capitalize() if obs.status else "Completed"

    def _add_organizer_entry(
        self,
        section: etree._Element,
        organizer: MentalStatusOrganizerProtocol,
    ) -> None:
        """
        Add entry element with Mental Status Organizer.

        Args:
            section: section element
            organizer: organizer data
        """
        # CONF:1198-28301, CONF:1198-28302
        entry = etree.SubElement(section, f"{{{NS}}}entry")

        # Create and add Mental Status Organizer
        organizer_builder = MentalStatusOrganizer(organizer, version=self.version)
        entry.append(organizer_builder.to_element())

    def _add_observation_entry(
        self,
        section: etree._Element,
        observation: MentalStatusObservationProtocol,
    ) -> None:
        """
        Add entry element with Mental Status Observation.

        Args:
            section: section element
            observation: observation data
        """
        # CONF:1198-28305, CONF:1198-28306
        entry = etree.SubElement(section, f"{{{NS}}}entry")

        # Create and add Mental Status Observation
        obs_builder = MentalStatusObservation(observation, version=self.version)
        entry.append(obs_builder.to_element())

Functions

__init__(observations=None, organizers=None, title='Mental Status', version=CDAVersion.R2_1, **kwargs)

Initialize MentalStatusSection builder.

Parameters:

Name Type Description Default
observations Sequence[MentalStatusObservationProtocol]

List of mental status observations (optional)

None
organizers Sequence[MentalStatusOrganizerProtocol]

List of mental status organizers (optional)

None
title str

Section title (default: "Mental Status")

'Mental Status'
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
**kwargs

Additional arguments passed to CDAElement

{}
Note

Either observations or organizers (or both) should be provided. If neither is provided, the section will show "No mental status observations recorded."

Source code in ccdakit/builders/sections/mental_status.py
def __init__(
    self,
    observations: Sequence[MentalStatusObservationProtocol] = None,
    organizers: Sequence[MentalStatusOrganizerProtocol] = None,
    title: str = "Mental Status",
    version: CDAVersion = CDAVersion.R2_1,
    **kwargs,
):
    """
    Initialize MentalStatusSection builder.

    Args:
        observations: List of mental status observations (optional)
        organizers: List of mental status organizers (optional)
        title: Section title (default: "Mental Status")
        version: C-CDA version (R2.1 or R2.0)
        **kwargs: Additional arguments passed to CDAElement

    Note:
        Either observations or organizers (or both) should be provided.
        If neither is provided, the section will show "No mental status observations recorded."
    """
    super().__init__(version=version, **kwargs)
    self.observations = observations or []
    self.organizers = organizers or []
    self.title = title

build()

Build Mental Status Section XML element.

Returns:

Type Description
Element

lxml Element for section

CONF Rules Implemented
  • CONF:1198-28293, CONF:1198-28294, CONF:1198-32793: templateId
  • CONF:1198-28295, CONF:1198-28296, CONF:1198-30826: code=10190-7 (LOINC)
  • CONF:1198-28297: title [1..1]
  • CONF:1198-28298: text [1..1]
  • CONF:1198-28301, CONF:1198-28302: entry with Mental Status Organizer [0..*]
  • CONF:1198-28305, CONF:1198-28306: entry with Mental Status Observation [0..*]
  • CONF:1198-28313, CONF:1198-28314: entry with Assessment Scale Observation [0..*]
Source code in ccdakit/builders/sections/mental_status.py
def build(self) -> etree.Element:
    """
    Build Mental Status Section XML element.

    Returns:
        lxml Element for section

    CONF Rules Implemented:
        - CONF:1198-28293, CONF:1198-28294, CONF:1198-32793: templateId
        - CONF:1198-28295, CONF:1198-28296, CONF:1198-30826: code=10190-7 (LOINC)
        - CONF:1198-28297: title [1..1]
        - CONF:1198-28298: text [1..1]
        - CONF:1198-28301, CONF:1198-28302: entry with Mental Status Organizer [0..*]
        - CONF:1198-28305, CONF:1198-28306: entry with Mental Status Observation [0..*]
        - CONF:1198-28313, CONF:1198-28314: entry with Assessment Scale Observation [0..*]
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # CONF:1198-28293, CONF:1198-28294, CONF:1198-32793: Add template IDs
    self.add_template_ids(section)

    # CONF:1198-28295, CONF:1198-28296, CONF:1198-30826: Add section code
    code_elem = Code(
        code="10190-7",
        system="LOINC",
        display_name="Mental Status",
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # CONF:1198-28297: Add title
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # CONF:1198-28298: Add narrative text (HTML table)
    self._add_narrative(section)

    # CONF:1198-28301, CONF:1198-28302: Add entries with Mental Status Organizers
    for organizer in self.organizers:
        self._add_organizer_entry(section, organizer)

    # CONF:1198-28305, CONF:1198-28306: Add entries with Mental Status Observations
    for observation in self.observations:
        self._add_observation_entry(section, observation)

    return section

NutritionSection

NutritionSection

Bases: CDAElement

Builder for C-CDA Nutrition Section.

Represents diet and nutrition information including special diet requirements and restrictions (e.g., texture modified diet, liquids only, enteral feeding). Also represents the overall nutritional status of the patient and nutrition assessment findings.

Template: 2.16.840.1.113883.10.20.22.2.57 Code: 61144-2 (Diet and nutrition) from LOINC

Conformance Rules: - CONF:1098-30477: SHALL contain templateId - CONF:1098-30478: templateId/@root="2.16.840.1.113883.10.20.22.2.57" - CONF:1098-30318: SHALL contain code - CONF:1098-30319: code/@code="61144-2" - CONF:1098-30320: code/@codeSystem="2.16.840.1.113883.6.1" (LOINC) - CONF:1098-31042: SHALL contain title - CONF:1098-31043: SHALL contain text - CONF:1098-30321: SHOULD contain zero or more [0..*] entry - CONF:1098-30322: entry SHALL contain Nutritional Status Observation

Includes narrative (HTML table) and structured entries. Supports both R2.1 and R2.0 versions.

Source code in ccdakit/builders/sections/nutrition.py
class NutritionSection(CDAElement):
    """
    Builder for C-CDA Nutrition Section.

    Represents diet and nutrition information including special diet requirements
    and restrictions (e.g., texture modified diet, liquids only, enteral feeding).
    Also represents the overall nutritional status of the patient and nutrition
    assessment findings.

    Template: 2.16.840.1.113883.10.20.22.2.57
    Code: 61144-2 (Diet and nutrition) from LOINC

    Conformance Rules:
    - CONF:1098-30477: SHALL contain templateId
    - CONF:1098-30478: templateId/@root="2.16.840.1.113883.10.20.22.2.57"
    - CONF:1098-30318: SHALL contain code
    - CONF:1098-30319: code/@code="61144-2"
    - CONF:1098-30320: code/@codeSystem="2.16.840.1.113883.6.1" (LOINC)
    - CONF:1098-31042: SHALL contain title
    - CONF:1098-31043: SHALL contain text
    - CONF:1098-30321: SHOULD contain zero or more [0..*] entry
    - CONF:1098-30322: entry SHALL contain Nutritional Status Observation

    Includes narrative (HTML table) and structured entries.
    Supports both R2.1 and R2.0 versions.
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.57",
                extension=None,
                description="Nutrition Section",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.57",
                extension=None,
                description="Nutrition Section",
            ),
        ],
    }

    # LOINC code for nutrition section
    NUTRITION_CODE = "61144-2"
    NUTRITION_DISPLAY = "Diet and nutrition"

    def __init__(
        self,
        nutritional_statuses: Sequence[NutritionalStatusProtocol],
        title: str = "Nutrition",
        version: CDAVersion = CDAVersion.R2_1,
        **kwargs,
    ):
        """
        Initialize NutritionSection builder.

        Args:
            nutritional_statuses: List of nutritional status observations
            title: Section title (default: "Nutrition")
            version: C-CDA version (R2.1 or R2.0)
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.nutritional_statuses = nutritional_statuses
        self.title = title

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

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add template IDs (CONF:1098-30477, CONF:1098-30478)
        self.add_template_ids(section)

        # Add section code (CONF:1098-30318, CONF:1098-30319, CONF:1098-30320)
        code_elem = Code(
            code=self.NUTRITION_CODE,
            system="LOINC",
            display_name=self.NUTRITION_DISPLAY,
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # Add title (CONF:1098-31042)
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (HTML table) (CONF:1098-31043)
        self._add_narrative(section)

        # Add entries with Nutritional Status Observations
        # (CONF:1098-30321, CONF:1098-30322)
        for status in self.nutritional_statuses:
            self._add_entry(section, status)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element with HTML table.

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        if not self.nutritional_statuses:
            # No nutritional statuses - add "No nutrition information available" paragraph
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            paragraph.text = "No nutrition information available"
            return

        # Create table for nutritional status
        table = etree.SubElement(text, f"{{{NS}}}table", border="1", width="100%")

        # Table header
        thead = etree.SubElement(table, f"{{{NS}}}thead")
        tr = etree.SubElement(thead, f"{{{NS}}}tr")

        headers = [
            "Nutritional Status",
            "Date Observed",
            "Assessment",
            "Value",
        ]
        for header_text in headers:
            th = etree.SubElement(tr, f"{{{NS}}}th")
            th.text = header_text

        # Table body
        tbody = etree.SubElement(table, f"{{{NS}}}tbody")

        for status_idx, status in enumerate(self.nutritional_statuses, start=1):
            # First row for the nutritional status
            if status.assessments:
                # If there are assessments, show them in subsequent rows
                for assessment_idx, assessment in enumerate(status.assessments, start=1):
                    tr = etree.SubElement(tbody, f"{{{NS}}}tr")

                    if assessment_idx == 1:
                        # First assessment row includes status info
                        # Nutritional Status (with ID reference and rowspan)
                        td_status = etree.SubElement(
                            tr,
                            f"{{{NS}}}td",
                            rowspan=str(len(status.assessments)),
                        )
                        content = etree.SubElement(
                            td_status,
                            f"{{{NS}}}content",
                            ID=f"nutrition-status-{status_idx}",
                        )
                        content.text = status.status

                        # Date Observed (with rowspan)
                        td_date = etree.SubElement(
                            tr,
                            f"{{{NS}}}td",
                            rowspan=str(len(status.assessments)),
                        )
                        # Format date/datetime appropriately
                        if hasattr(status.date, "strftime"):
                            if hasattr(status.date, "hour"):
                                # It's a datetime
                                td_date.text = status.date.strftime("%Y-%m-%d %H:%M")
                            else:
                                # It's a date
                                td_date.text = status.date.strftime("%Y-%m-%d")
                        else:
                            td_date.text = str(status.date)

                    # Assessment
                    td_assessment = etree.SubElement(tr, f"{{{NS}}}td")
                    td_assessment.text = assessment.assessment_type

                    # Value
                    td_value = etree.SubElement(tr, f"{{{NS}}}td")
                    td_value.text = assessment.value
            else:
                # No assessments, just show status
                tr = etree.SubElement(tbody, f"{{{NS}}}tr")

                # Nutritional Status (with ID reference)
                td_status = etree.SubElement(tr, f"{{{NS}}}td")
                content = etree.SubElement(
                    td_status,
                    f"{{{NS}}}content",
                    ID=f"nutrition-status-{status_idx}",
                )
                content.text = status.status

                # Date Observed
                td_date = etree.SubElement(tr, f"{{{NS}}}td")
                if hasattr(status.date, "strftime"):
                    if hasattr(status.date, "hour"):
                        td_date.text = status.date.strftime("%Y-%m-%d %H:%M")
                    else:
                        td_date.text = status.date.strftime("%Y-%m-%d")
                else:
                    td_date.text = str(status.date)

                # Empty cells for assessment and value
                td_assessment = etree.SubElement(tr, f"{{{NS}}}td")
                td_assessment.text = "-"
                td_value = etree.SubElement(tr, f"{{{NS}}}td")
                td_value.text = "-"

    def _add_entry(self, section: etree._Element, status: NutritionalStatusProtocol) -> None:
        """
        Add entry element with Nutritional Status Observation.

        CONF:1098-30321: SHOULD contain zero or more [0..*] entry
        CONF:1098-30322: entry SHALL contain Nutritional Status Observation

        Args:
            section: section element
            status: Nutritional status data
        """
        # Create entry element
        entry = etree.SubElement(section, f"{{{NS}}}entry")

        # Create and add Nutritional Status Observation (CONF:1098-30322)
        obs_builder = NutritionalStatusObservation(status, version=self.version)
        entry.append(obs_builder.to_element())

Functions

__init__(nutritional_statuses, title='Nutrition', version=CDAVersion.R2_1, **kwargs)

Initialize NutritionSection builder.

Parameters:

Name Type Description Default
nutritional_statuses Sequence[NutritionalStatusProtocol]

List of nutritional status observations

required
title str

Section title (default: "Nutrition")

'Nutrition'
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/nutrition.py
def __init__(
    self,
    nutritional_statuses: Sequence[NutritionalStatusProtocol],
    title: str = "Nutrition",
    version: CDAVersion = CDAVersion.R2_1,
    **kwargs,
):
    """
    Initialize NutritionSection builder.

    Args:
        nutritional_statuses: List of nutritional status observations
        title: Section title (default: "Nutrition")
        version: C-CDA version (R2.1 or R2.0)
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.nutritional_statuses = nutritional_statuses
    self.title = title

build()

Build Nutrition Section XML element.

Returns:

Type Description
Element

lxml Element for section

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

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add template IDs (CONF:1098-30477, CONF:1098-30478)
    self.add_template_ids(section)

    # Add section code (CONF:1098-30318, CONF:1098-30319, CONF:1098-30320)
    code_elem = Code(
        code=self.NUTRITION_CODE,
        system="LOINC",
        display_name=self.NUTRITION_DISPLAY,
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # Add title (CONF:1098-31042)
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (HTML table) (CONF:1098-31043)
    self._add_narrative(section)

    # Add entries with Nutritional Status Observations
    # (CONF:1098-30321, CONF:1098-30322)
    for status in self.nutritional_statuses:
        self._add_entry(section, status)

    return section

PastMedicalHistorySection

PastMedicalHistorySection

Bases: CDAElement

Builder for C-CDA Past Medical History Section.

This section contains a record of the patient's past complaints, problems, and diagnoses. It contains data from the patient's past up to the patient's current complaint or reason for seeking medical care.

Includes narrative (HTML table) and structured entries with Problem Observations. Supports both R2.1 (2015-08-01) and R2.0 versions.

Conformance: - Template ID: 2.16.840.1.113883.10.20.22.2.20 - Code: 11348-0 (History of Past Illness) from LOINC - Contains: Problem Observation entries (optional, 0..*)

Source code in ccdakit/builders/sections/past_medical_history.py
class PastMedicalHistorySection(CDAElement):
    """
    Builder for C-CDA Past Medical History Section.

    This section contains a record of the patient's past complaints, problems,
    and diagnoses. It contains data from the patient's past up to the patient's
    current complaint or reason for seeking medical care.

    Includes narrative (HTML table) and structured entries with Problem Observations.
    Supports both R2.1 (2015-08-01) and R2.0 versions.

    Conformance:
    - Template ID: 2.16.840.1.113883.10.20.22.2.20
    - Code: 11348-0 (History of Past Illness) from LOINC
    - Contains: Problem Observation entries (optional, 0..*)
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.20",
                extension="2015-08-01",
                description="Past Medical History Section (V3) R2.1",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.20",
                extension=None,  # R2.0 may not have extension
                description="Past Medical History Section R2.0",
            ),
        ],
    }

    def __init__(
        self,
        problems: Sequence[ProblemProtocol],
        title: str = "Past Medical History",
        version: CDAVersion = CDAVersion.R2_1,
        **kwargs,
    ):
        """
        Initialize PastMedicalHistorySection builder.

        Args:
            problems: List of problems satisfying ProblemProtocol
            title: Section title (default: "Past Medical History")
            version: C-CDA version (R2.1 or R2.0)
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.problems = problems
        self.title = title

    def build(self) -> etree.Element:
        """
        Build Past Medical History Section XML element.

        Conformance:
        - CONF:1198-7828: SHALL contain templateId
        - CONF:1198-10390: templateId/@root="2.16.840.1.113883.10.20.22.2.20"
        - CONF:1198-32536: templateId/@extension="2015-08-01" (R2.1)
        - CONF:1198-15474: SHALL contain code
        - CONF:1198-15475: code/@code="11348-0"
        - CONF:1198-30831: code/@codeSystem="2.16.840.1.113883.6.1" (LOINC)
        - CONF:1198-7830: SHALL contain title
        - CONF:1198-7831: SHALL contain text
        - CONF:1198-8791: MAY contain entries
        - CONF:1198-15476: entry SHALL contain Problem Observation

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add template IDs (CONF:1198-7828, CONF:1198-10390, CONF:1198-32536)
        self.add_template_ids(section)

        # Add section code (CONF:1198-15474, CONF:1198-15475, CONF:1198-30831)
        # 11348-0 = History of Past Illness (LOINC)
        code_elem = Code(
            code="11348-0",
            system="LOINC",
            display_name="History of Past Illness",
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # Add title (CONF:1198-7830)
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (HTML table) (CONF:1198-7831)
        self._add_narrative(section)

        # Add entries with Problem Observations (CONF:1198-8791, CONF:1198-15476)
        for problem in self.problems:
            self._add_entry(section, problem)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element with HTML table.

        The narrative provides human-readable content for the section.
        When no problems are present, displays "No past medical history".

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        if not self.problems:
            # No problems - add "No past medical history" paragraph
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            paragraph.text = "No past medical history"
            return

        # Create table for problems
        table = etree.SubElement(text, f"{{{NS}}}table", border="1", width="100%")

        # Table header
        thead = etree.SubElement(table, f"{{{NS}}}thead")
        tr = etree.SubElement(thead, f"{{{NS}}}tr")

        headers = ["Problem", "Code", "Status", "Onset Date", "Resolved Date"]
        for header_text in headers:
            th = etree.SubElement(tr, f"{{{NS}}}th")
            th.text = header_text

        # Table body
        tbody = etree.SubElement(table, f"{{{NS}}}tbody")

        for idx, problem in enumerate(self.problems, start=1):
            tr = etree.SubElement(tbody, f"{{{NS}}}tr")

            # Problem name (with ID reference for linking)
            td_name = etree.SubElement(tr, f"{{{NS}}}td")
            content = etree.SubElement(
                td_name,
                f"{{{NS}}}content",
                ID=f"pmh-problem-{idx}",
            )
            content.text = problem.name

            # Code with system
            td_code = etree.SubElement(tr, f"{{{NS}}}td")
            td_code.text = f"{problem.code} ({problem.code_system})"

            # Status
            td_status = etree.SubElement(tr, f"{{{NS}}}td")
            td_status.text = problem.status.capitalize()

            # Onset date
            td_onset = etree.SubElement(tr, f"{{{NS}}}td")
            if problem.onset_date:
                td_onset.text = problem.onset_date.strftime("%Y-%m-%d")
            else:
                td_onset.text = "Unknown"

            # Resolved date
            td_resolved = etree.SubElement(tr, f"{{{NS}}}td")
            if problem.resolved_date:
                td_resolved.text = problem.resolved_date.strftime("%Y-%m-%d")
            else:
                # Show different text based on status
                if problem.status.lower() == "active":
                    td_resolved.text = "Ongoing"
                elif problem.status.lower() == "resolved":
                    td_resolved.text = "Unknown"
                else:
                    td_resolved.text = "-"

    def _add_entry(self, section: etree._Element, problem: ProblemProtocol) -> None:
        """
        Add entry element with Problem Observation.

        Per the spec (CONF:1198-8791, CONF:1198-15476), each entry SHALL contain
        a Problem Observation. Unlike the Problems Section which wraps observations
        in a Problem Concern Act, the Past Medical History Section typically contains
        Problem Observations directly.

        Args:
            section: section element
            problem: Problem data
        """
        # Create entry element (CONF:1198-8791)
        entry = etree.SubElement(section, f"{{{NS}}}entry")

        # Create and add Problem Observation (CONF:1198-15476)
        obs_builder = ProblemObservation(problem, version=self.version)
        entry.append(obs_builder.to_element())

Functions

__init__(problems, title='Past Medical History', version=CDAVersion.R2_1, **kwargs)

Initialize PastMedicalHistorySection builder.

Parameters:

Name Type Description Default
problems Sequence[ProblemProtocol]

List of problems satisfying ProblemProtocol

required
title str

Section title (default: "Past Medical History")

'Past Medical History'
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/past_medical_history.py
def __init__(
    self,
    problems: Sequence[ProblemProtocol],
    title: str = "Past Medical History",
    version: CDAVersion = CDAVersion.R2_1,
    **kwargs,
):
    """
    Initialize PastMedicalHistorySection builder.

    Args:
        problems: List of problems satisfying ProblemProtocol
        title: Section title (default: "Past Medical History")
        version: C-CDA version (R2.1 or R2.0)
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.problems = problems
    self.title = title

build()

Build Past Medical History Section XML element.

Conformance: - CONF:1198-7828: SHALL contain templateId - CONF:1198-10390: templateId/@root="2.16.840.1.113883.10.20.22.2.20" - CONF:1198-32536: templateId/@extension="2015-08-01" (R2.1) - CONF:1198-15474: SHALL contain code - CONF:1198-15475: code/@code="11348-0" - CONF:1198-30831: code/@codeSystem="2.16.840.1.113883.6.1" (LOINC) - CONF:1198-7830: SHALL contain title - CONF:1198-7831: SHALL contain text - CONF:1198-8791: MAY contain entries - CONF:1198-15476: entry SHALL contain Problem Observation

Returns:

Type Description
Element

lxml Element for section

Source code in ccdakit/builders/sections/past_medical_history.py
def build(self) -> etree.Element:
    """
    Build Past Medical History Section XML element.

    Conformance:
    - CONF:1198-7828: SHALL contain templateId
    - CONF:1198-10390: templateId/@root="2.16.840.1.113883.10.20.22.2.20"
    - CONF:1198-32536: templateId/@extension="2015-08-01" (R2.1)
    - CONF:1198-15474: SHALL contain code
    - CONF:1198-15475: code/@code="11348-0"
    - CONF:1198-30831: code/@codeSystem="2.16.840.1.113883.6.1" (LOINC)
    - CONF:1198-7830: SHALL contain title
    - CONF:1198-7831: SHALL contain text
    - CONF:1198-8791: MAY contain entries
    - CONF:1198-15476: entry SHALL contain Problem Observation

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add template IDs (CONF:1198-7828, CONF:1198-10390, CONF:1198-32536)
    self.add_template_ids(section)

    # Add section code (CONF:1198-15474, CONF:1198-15475, CONF:1198-30831)
    # 11348-0 = History of Past Illness (LOINC)
    code_elem = Code(
        code="11348-0",
        system="LOINC",
        display_name="History of Past Illness",
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # Add title (CONF:1198-7830)
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (HTML table) (CONF:1198-7831)
    self._add_narrative(section)

    # Add entries with Problem Observations (CONF:1198-8791, CONF:1198-15476)
    for problem in self.problems:
        self._add_entry(section, problem)

    return section

PayersSection

PayersSection

Bases: CDAElement

Builder for C-CDA Payers Section.

The Payers Section contains data on the patient's payers, whether third party insurance, self-pay, other payer or guarantor. Each unique instance of a payer and all pertinent data needed to contact, bill to, and collect from that payer should be included.

Includes narrative (HTML table) and structured entries (Coverage Activities).

Conforms to: - Template ID: 2.16.840.1.113883.10.20.22.2.18 (Payers Section V3) - Extension: 2015-08-01 - LOINC Code: 48768-6 "Payers"

Supports both R2.1 and R2.0 versions.

Source code in ccdakit/builders/sections/payers.py
class PayersSection(CDAElement):
    """
    Builder for C-CDA Payers Section.

    The Payers Section contains data on the patient's payers, whether third party
    insurance, self-pay, other payer or guarantor. Each unique instance of a payer
    and all pertinent data needed to contact, bill to, and collect from that payer
    should be included.

    Includes narrative (HTML table) and structured entries (Coverage Activities).

    Conforms to:
    - Template ID: 2.16.840.1.113883.10.20.22.2.18 (Payers Section V3)
    - Extension: 2015-08-01
    - LOINC Code: 48768-6 "Payers"

    Supports both R2.1 and R2.0 versions.
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.18",
                extension="2015-08-01",
                description="Payers Section (V3) R2.1",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.18",
                extension="2015-08-01",
                description="Payers Section (V3) R2.0",
            ),
        ],
    }

    def __init__(
        self,
        payers: Sequence[PayerProtocol],
        title: str = "Insurance Providers",
        version: CDAVersion = CDAVersion.R2_1,
        **kwargs,
    ):
        """
        Initialize PayersSection builder.

        Args:
            payers: List of payers satisfying PayerProtocol
            title: Section title (default: "Insurance Providers")
            version: C-CDA version (R2.1 or R2.0)
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.payers = payers
        self.title = title

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

        Conforms to all CONF rules from template 2.16.840.1.113883.10.20.22.2.18:
        - CONF:1198-7924/10434/32597: templateId
        - CONF:1198-15395/15396/32149: code="48768-6" Payers
        - CONF:1198-7926: title
        - CONF:1198-7927: text
        - CONF:1198-7959/15501: entry with Coverage Activity (SHOULD)

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add template IDs (CONF:1198-7924, CONF:1198-10434, CONF:1198-32597)
        self.add_template_ids(section)

        # Add section code (CONF:1198-15395, CONF:1198-15396, CONF:1198-32149)
        code_elem = Code(
            code="48768-6",
            system="LOINC",
            display_name="Payers",
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # Add title (CONF:1198-7926)
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (HTML table) (CONF:1198-7927)
        self._add_narrative(section)

        # Add entries with Coverage Activities (CONF:1198-7959, CONF:1198-15501)
        for payer in self.payers:
            self._add_entry(section, payer)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element with HTML table.

        Generates a human-readable table of payer information.

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        if not self.payers:
            # No payers - add "No insurance information" paragraph
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            paragraph.text = "No insurance information available"
            return

        # Create table for payers
        table = etree.SubElement(text, f"{{{NS}}}table", border="1", width="100%")

        # Table header
        thead = etree.SubElement(table, f"{{{NS}}}thead")
        tr = etree.SubElement(thead, f"{{{NS}}}tr")

        headers = [
            "Payer Name",
            "Insurance Type",
            "Member ID",
            "Group Number",
            "Coverage Period",
            "Priority",
        ]
        for header_text in headers:
            th = etree.SubElement(tr, f"{{{NS}}}th")
            th.text = header_text

        # Table body
        tbody = etree.SubElement(table, f"{{{NS}}}tbody")

        for idx, payer in enumerate(self.payers, start=1):
            tr = etree.SubElement(tbody, f"{{{NS}}}tr")

            # Payer name (with ID reference)
            td_payer = etree.SubElement(tr, f"{{{NS}}}td")
            content = etree.SubElement(
                td_payer,
                f"{{{NS}}}content",
                ID=f"payer-{idx}",
            )
            content.text = payer.payer_name

            # Insurance type
            td_type = etree.SubElement(tr, f"{{{NS}}}td")
            td_type.text = payer.insurance_type

            # Member ID
            td_member = etree.SubElement(tr, f"{{{NS}}}td")
            td_member.text = payer.member_id

            # Group number
            td_group = etree.SubElement(tr, f"{{{NS}}}td")
            td_group.text = payer.group_number if payer.group_number else "N/A"

            # Coverage period
            td_period = etree.SubElement(tr, f"{{{NS}}}td")
            if payer.start_date:
                period_text = payer.start_date.strftime("%Y-%m-%d")
                if payer.end_date:
                    period_text += f" to {payer.end_date.strftime('%Y-%m-%d')}"
                else:
                    period_text += " to present"
                td_period.text = period_text
            else:
                td_period.text = "Unknown"

            # Priority/sequence
            td_priority = etree.SubElement(tr, f"{{{NS}}}td")
            if payer.sequence_number is not None:
                priority_map = {1: "Primary", 2: "Secondary", 3: "Tertiary"}
                td_priority.text = priority_map.get(
                    payer.sequence_number, f"Priority {payer.sequence_number}"
                )
            else:
                td_priority.text = "Not specified"

    def _add_entry(self, section: etree._Element, payer: PayerProtocol) -> None:
        """
        Add entry element with Coverage Activity.

        Conforms to:
        - CONF:1198-7959: SHOULD contain entry
        - CONF:1198-15501: SHALL contain Coverage Activity

        Args:
            section: section element
            payer: Payer data
        """
        # Create entry element
        entry = etree.SubElement(section, f"{{{NS}}}entry", typeCode="DRIV")

        # Create and add Coverage Activity (CONF:1198-15501)
        coverage_builder = CoverageActivity(payer, version=self.version)
        entry.append(coverage_builder.to_element())

Functions

__init__(payers, title='Insurance Providers', version=CDAVersion.R2_1, **kwargs)

Initialize PayersSection builder.

Parameters:

Name Type Description Default
payers Sequence[PayerProtocol]

List of payers satisfying PayerProtocol

required
title str

Section title (default: "Insurance Providers")

'Insurance Providers'
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/payers.py
def __init__(
    self,
    payers: Sequence[PayerProtocol],
    title: str = "Insurance Providers",
    version: CDAVersion = CDAVersion.R2_1,
    **kwargs,
):
    """
    Initialize PayersSection builder.

    Args:
        payers: List of payers satisfying PayerProtocol
        title: Section title (default: "Insurance Providers")
        version: C-CDA version (R2.1 or R2.0)
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.payers = payers
    self.title = title

build()

Build Payers Section XML element.

Conforms to all CONF rules from template 2.16.840.1.113883.10.20.22.2.18: - CONF:1198-7924/10434/32597: templateId - CONF:1198-15395/15396/32149: code="48768-6" Payers - CONF:1198-7926: title - CONF:1198-7927: text - CONF:1198-7959/15501: entry with Coverage Activity (SHOULD)

Returns:

Type Description
Element

lxml Element for section

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

    Conforms to all CONF rules from template 2.16.840.1.113883.10.20.22.2.18:
    - CONF:1198-7924/10434/32597: templateId
    - CONF:1198-15395/15396/32149: code="48768-6" Payers
    - CONF:1198-7926: title
    - CONF:1198-7927: text
    - CONF:1198-7959/15501: entry with Coverage Activity (SHOULD)

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add template IDs (CONF:1198-7924, CONF:1198-10434, CONF:1198-32597)
    self.add_template_ids(section)

    # Add section code (CONF:1198-15395, CONF:1198-15396, CONF:1198-32149)
    code_elem = Code(
        code="48768-6",
        system="LOINC",
        display_name="Payers",
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # Add title (CONF:1198-7926)
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (HTML table) (CONF:1198-7927)
    self._add_narrative(section)

    # Add entries with Coverage Activities (CONF:1198-7959, CONF:1198-15501)
    for payer in self.payers:
        self._add_entry(section, payer)

    return section

PhysicalExamSection

PhysicalExamSection

Bases: CDAElement

Builder for C-CDA Physical Exam Section (V3).

The section includes direct observations made by a clinician. The examination may include the use of simple instruments and may also describe simple maneuvers performed directly on the patient's body.

Supports both R2.1 (2015-08-01) and R2.0 (2015-08-01) versions.

Conforms to: - Physical Exam Section (V3): 2.16.840.1.113883.10.20.2.10:2015-08-01

Source code in ccdakit/builders/sections/physical_exam.py
class PhysicalExamSection(CDAElement):
    """
    Builder for C-CDA Physical Exam Section (V3).

    The section includes direct observations made by a clinician. The examination
    may include the use of simple instruments and may also describe simple maneuvers
    performed directly on the patient's body.

    Supports both R2.1 (2015-08-01) and R2.0 (2015-08-01) versions.

    Conforms to:
    - Physical Exam Section (V3): 2.16.840.1.113883.10.20.2.10:2015-08-01
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.2.10",
                extension="2015-08-01",
                description="Physical Exam Section (V3)",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.2.10",
                extension="2015-08-01",
                description="Physical Exam Section (V3)",
            ),
        ],
    }

    def __init__(
        self,
        wound_observations: Optional[Sequence[WoundObservationProtocol]] = None,
        title: str = "Physical Exam",
        text: Optional[str] = None,
        version: CDAVersion = CDAVersion.R2_1,
        **kwargs,
    ):
        """
        Initialize PhysicalExamSection builder.

        Args:
            wound_observations: Optional list of wound observations
            title: Section title (default: "Physical Exam")
            text: Optional narrative text content
            version: C-CDA version (R2.1 or R2.0)
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.wound_observations = wound_observations or []
        self.title = title
        self.text = text

    def build(self) -> etree.Element:
        """
        Build Physical Exam Section XML element.

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add template ID (CONF:1198-7806, CONF:1198-10465, CONF:1198-32587)
        self.add_template_ids(section)

        # Add section code (CONF:1198-15397, CONF:1198-15398, CONF:1198-30931)
        code_elem = Code(
            code="29545-1",
            system="LOINC",
            display_name="Physical Findings",
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # Add title (CONF:1198-7808)
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (CONF:1198-7809)
        self._add_narrative(section)

        # Add entries with Wound Observations (CONF:1198-31926, CONF:1198-31927)
        for wound_obs in self.wound_observations:
            self._add_entry(section, wound_obs)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element (CONF:1198-7809).

        Args:
            section: section element
        """
        # Create text element
        text_elem = etree.SubElement(section, f"{{{NS}}}text")

        if self.text:
            # Use provided narrative text
            text_elem.text = self.text
        elif not self.wound_observations:
            # No observations - add default paragraph
            paragraph = etree.SubElement(text_elem, f"{{{NS}}}paragraph")
            paragraph.text = "No physical exam findings recorded"
        else:
            # Create table for wound observations
            table = etree.SubElement(text_elem, f"{{{NS}}}table", border="1", width="100%")

            # Table header
            thead = etree.SubElement(table, f"{{{NS}}}thead")
            tr = etree.SubElement(thead, f"{{{NS}}}tr")

            headers = [
                "Date/Time",
                "Wound Type",
                "Location",
                "Laterality",
            ]
            for header_text in headers:
                th = etree.SubElement(tr, f"{{{NS}}}th")
                th.text = header_text

            # Table body
            tbody = etree.SubElement(table, f"{{{NS}}}tbody")

            for obs_idx, wound_obs in enumerate(self.wound_observations, start=1):
                tr = etree.SubElement(tbody, f"{{{NS}}}tr")

                # Date/Time
                td_date = etree.SubElement(tr, f"{{{NS}}}td")
                if hasattr(wound_obs.date, "strftime"):
                    td_date.text = wound_obs.date.strftime("%Y-%m-%d %H:%M")
                else:
                    td_date.text = str(wound_obs.date)

                # Wound Type (with ID reference)
                td_type = etree.SubElement(tr, f"{{{NS}}}td")
                content = etree.SubElement(
                    td_type,
                    f"{{{NS}}}content",
                    ID=f"wound-{obs_idx}",
                )
                content.text = wound_obs.wound_type

                # Location
                td_location = etree.SubElement(tr, f"{{{NS}}}td")
                if wound_obs.location:
                    td_location.text = wound_obs.location
                else:
                    td_location.text = "-"

                # Laterality
                td_laterality = etree.SubElement(tr, f"{{{NS}}}td")
                if wound_obs.laterality:
                    td_laterality.text = wound_obs.laterality
                else:
                    td_laterality.text = "-"

    def _add_entry(self, section: etree._Element, wound_obs: WoundObservationProtocol) -> None:
        """
        Add entry element with Wound Observation (CONF:1198-31926, CONF:1198-31927).

        Args:
            section: section element
            wound_obs: Wound observation data
        """
        # Create entry element
        entry = etree.SubElement(section, f"{{{NS}}}entry")

        # Create and add Wound Observation
        observation_builder = LongitudinalCareWoundObservation(wound_obs, version=self.version)
        entry.append(observation_builder.to_element())

Functions

__init__(wound_observations=None, title='Physical Exam', text=None, version=CDAVersion.R2_1, **kwargs)

Initialize PhysicalExamSection builder.

Parameters:

Name Type Description Default
wound_observations Optional[Sequence[WoundObservationProtocol]]

Optional list of wound observations

None
title str

Section title (default: "Physical Exam")

'Physical Exam'
text Optional[str]

Optional narrative text content

None
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/physical_exam.py
def __init__(
    self,
    wound_observations: Optional[Sequence[WoundObservationProtocol]] = None,
    title: str = "Physical Exam",
    text: Optional[str] = None,
    version: CDAVersion = CDAVersion.R2_1,
    **kwargs,
):
    """
    Initialize PhysicalExamSection builder.

    Args:
        wound_observations: Optional list of wound observations
        title: Section title (default: "Physical Exam")
        text: Optional narrative text content
        version: C-CDA version (R2.1 or R2.0)
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.wound_observations = wound_observations or []
    self.title = title
    self.text = text

build()

Build Physical Exam Section XML element.

Returns:

Type Description
Element

lxml Element for section

Source code in ccdakit/builders/sections/physical_exam.py
def build(self) -> etree.Element:
    """
    Build Physical Exam Section XML element.

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add template ID (CONF:1198-7806, CONF:1198-10465, CONF:1198-32587)
    self.add_template_ids(section)

    # Add section code (CONF:1198-15397, CONF:1198-15398, CONF:1198-30931)
    code_elem = Code(
        code="29545-1",
        system="LOINC",
        display_name="Physical Findings",
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # Add title (CONF:1198-7808)
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (CONF:1198-7809)
    self._add_narrative(section)

    # Add entries with Wound Observations (CONF:1198-31926, CONF:1198-31927)
    for wound_obs in self.wound_observations:
        self._add_entry(section, wound_obs)

    return section

PlanOfTreatmentSection

PlanOfTreatmentSection

Bases: CDAElement

Builder for C-CDA Plan of Treatment Section (V2).

Includes narrative (HTML table) and structured entries for planned activities. Supports both R2.1 (2015-08-01) and R2.0 (2014-06-09) versions. Template ID: 2.16.840.1.113883.10.20.22.2.10

This section contains pending orders, interventions, encounters, services, and procedures for the patient. All entries use moodCode of INT (intent) or other prospective mood codes.

Source code in ccdakit/builders/sections/plan_of_treatment.py
class PlanOfTreatmentSection(CDAElement):
    """
    Builder for C-CDA Plan of Treatment Section (V2).

    Includes narrative (HTML table) and structured entries for planned activities.
    Supports both R2.1 (2015-08-01) and R2.0 (2014-06-09) versions.
    Template ID: 2.16.840.1.113883.10.20.22.2.10

    This section contains pending orders, interventions, encounters, services,
    and procedures for the patient. All entries use moodCode of INT (intent)
    or other prospective mood codes.
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.10",
                extension="2015-08-01",
                description="Plan of Treatment Section (V2) R2.1",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.10",
                extension="2014-06-09",
                description="Plan of Treatment Section (V2) R2.0",
            ),
        ],
    }

    def __init__(
        self,
        planned_observations: Optional[Sequence[PlannedObservationProtocol]] = None,
        planned_procedures: Optional[Sequence[PlannedProcedureProtocol]] = None,
        planned_encounters: Optional[Sequence[PlannedEncounterProtocol]] = None,
        planned_acts: Optional[Sequence[PlannedActProtocol]] = None,
        planned_medications: Optional[Sequence[PlannedMedicationProtocol]] = None,
        planned_supplies: Optional[Sequence[PlannedSupplyProtocol]] = None,
        planned_immunizations: Optional[Sequence[PlannedImmunizationProtocol]] = None,
        instructions: Optional[Sequence[InstructionProtocol]] = None,
        title: str = "Plan of Treatment",
        version: CDAVersion = CDAVersion.R2_1,
        **kwargs,
    ):
        """
        Initialize PlanOfTreatmentSection builder.

        Args:
            planned_observations: List of planned observations
            planned_procedures: List of planned procedures
            planned_encounters: List of planned encounters
            planned_acts: List of planned acts
            planned_medications: List of planned medications
            planned_supplies: List of planned supplies
            planned_immunizations: List of planned immunizations
            instructions: List of instructions
            title: Section title (default: "Plan of Treatment")
            version: C-CDA version (R2.1 or R2.0)
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.planned_observations = planned_observations or []
        self.planned_procedures = planned_procedures or []
        self.planned_encounters = planned_encounters or []
        self.planned_acts = planned_acts or []
        self.planned_medications = planned_medications or []
        self.planned_supplies = planned_supplies or []
        self.planned_immunizations = planned_immunizations or []
        self.instructions = instructions or []
        self.title = title

    def build(self) -> etree.Element:
        """
        Build Plan of Treatment Section XML element.

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add template IDs (CONF:1098-7723, CONF:1098-10435, CONF:1098-32501)
        self.add_template_ids(section)

        # Add section code (CONF:1098-14749, CONF:1098-14750, CONF:1098-30813)
        code_elem = Code(
            code="18776-5",
            system="LOINC",
            display_name="Plan of Treatment",
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # Add title (CONF:1098-16986)
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (CONF:1098-7725)
        self._add_narrative(section)

        # Add entries for each type of planned activity
        # Planned Observations (CONF:1098-7726, CONF:1098-14751)
        for obs in self.planned_observations:
            self._add_entry(section, obs, PlannedObservation)

        # Planned Encounters (CONF:1098-8805, CONF:1098-30472)
        for enc in self.planned_encounters:
            self._add_entry(section, enc, PlannedEncounter)

        # Planned Acts (CONF:1098-8807, CONF:1098-30473)
        for act in self.planned_acts:
            self._add_entry(section, act, PlannedAct)

        # Planned Procedures (CONF:1098-8809, CONF:1098-30474)
        for proc in self.planned_procedures:
            self._add_entry(section, proc, PlannedProcedure)

        # Planned Medications (CONF:1098-8811, CONF:1098-30475)
        for med in self.planned_medications:
            self._add_entry(section, med, PlannedMedication)

        # Planned Supplies (CONF:1098-8813, CONF:1098-30476)
        for supply in self.planned_supplies:
            self._add_entry(section, supply, PlannedSupply)

        # Instructions (CONF:1098-14695, CONF:1098-31397)
        for instruction in self.instructions:
            self._add_entry(section, instruction, Instruction)

        # Planned Immunizations (CONF:1098-32353, CONF:1098-32354)
        for immunization in self.planned_immunizations:
            self._add_entry(section, immunization, PlannedImmunization)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element with HTML table.

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        # Collect all activities
        all_activities = []

        for obs in self.planned_observations:
            all_activities.append(("Observation", obs))
        for proc in self.planned_procedures:
            all_activities.append(("Procedure", proc))
        for enc in self.planned_encounters:
            all_activities.append(("Encounter", enc))
        for act in self.planned_acts:
            all_activities.append(("Act", act))
        for med in self.planned_medications:
            all_activities.append(("Medication", med))
        for supply in self.planned_supplies:
            all_activities.append(("Supply", supply))
        for immunization in self.planned_immunizations:
            all_activities.append(("Immunization", immunization))
        for instruction in self.instructions:
            all_activities.append(("Instruction", instruction))

        if not all_activities:
            # No planned activities - add "No planned activities" paragraph
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            paragraph.text = "No planned activities"
            return

        # Create table for planned activities
        table = etree.SubElement(text, f"{{{NS}}}table", border="1", width="100%")

        # Table header
        thead = etree.SubElement(table, f"{{{NS}}}thead")
        tr = etree.SubElement(thead, f"{{{NS}}}tr")

        headers = ["Type", "Description", "Code", "Status", "Planned Date"]
        for header_text in headers:
            th = etree.SubElement(tr, f"{{{NS}}}th")
            th.text = header_text

        # Table body
        tbody = etree.SubElement(table, f"{{{NS}}}tbody")

        for idx, (activity_type, activity) in enumerate(all_activities, start=1):
            tr = etree.SubElement(tbody, f"{{{NS}}}tr")

            # Activity type
            td_type = etree.SubElement(tr, f"{{{NS}}}td")
            td_type.text = activity_type

            # Description (with ID reference)
            td_desc = etree.SubElement(tr, f"{{{NS}}}td")
            content = etree.SubElement(
                td_desc,
                f"{{{NS}}}content",
                ID=f"planned-activity-{idx}",
            )
            # Check if it's an instruction by checking for instruction_text attribute
            if hasattr(activity, "instruction_text"):
                content.text = activity.instruction_text
            else:
                content.text = activity.description

            # Code
            td_code = etree.SubElement(tr, f"{{{NS}}}td")
            if hasattr(activity, "code") and activity.code:
                code_system = getattr(activity, "code_system", "Unknown")
                td_code.text = f"{activity.code} ({code_system})"
            else:
                td_code.text = "N/A"

            # Status
            td_status = etree.SubElement(tr, f"{{{NS}}}td")
            if hasattr(activity, "status"):
                td_status.text = activity.status.capitalize()
            else:
                td_status.text = "N/A"

            # Planned date
            td_date = etree.SubElement(tr, f"{{{NS}}}td")
            if hasattr(activity, "planned_date") and activity.planned_date:
                td_date.text = activity.planned_date.strftime("%Y-%m-%d")
            else:
                td_date.text = "Not specified"

    def _add_entry(
        self,
        section: etree._Element,
        activity: PlannedActivityType,
        builder_class: type,
    ) -> None:
        """
        Add entry element with planned activity.

        Args:
            section: section element
            activity: Planned activity data
            builder_class: Builder class to use for creating entry
        """
        # Create entry element
        entry = etree.SubElement(section, f"{{{NS}}}entry")

        # Create and add activity element
        activity_builder = builder_class(activity, version=self.version)
        entry.append(activity_builder.to_element())

Functions

__init__(planned_observations=None, planned_procedures=None, planned_encounters=None, planned_acts=None, planned_medications=None, planned_supplies=None, planned_immunizations=None, instructions=None, title='Plan of Treatment', version=CDAVersion.R2_1, **kwargs)

Initialize PlanOfTreatmentSection builder.

Parameters:

Name Type Description Default
planned_observations Optional[Sequence[PlannedObservationProtocol]]

List of planned observations

None
planned_procedures Optional[Sequence[PlannedProcedureProtocol]]

List of planned procedures

None
planned_encounters Optional[Sequence[PlannedEncounterProtocol]]

List of planned encounters

None
planned_acts Optional[Sequence[PlannedActProtocol]]

List of planned acts

None
planned_medications Optional[Sequence[PlannedMedicationProtocol]]

List of planned medications

None
planned_supplies Optional[Sequence[PlannedSupplyProtocol]]

List of planned supplies

None
planned_immunizations Optional[Sequence[PlannedImmunizationProtocol]]

List of planned immunizations

None
instructions Optional[Sequence[InstructionProtocol]]

List of instructions

None
title str

Section title (default: "Plan of Treatment")

'Plan of Treatment'
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/plan_of_treatment.py
def __init__(
    self,
    planned_observations: Optional[Sequence[PlannedObservationProtocol]] = None,
    planned_procedures: Optional[Sequence[PlannedProcedureProtocol]] = None,
    planned_encounters: Optional[Sequence[PlannedEncounterProtocol]] = None,
    planned_acts: Optional[Sequence[PlannedActProtocol]] = None,
    planned_medications: Optional[Sequence[PlannedMedicationProtocol]] = None,
    planned_supplies: Optional[Sequence[PlannedSupplyProtocol]] = None,
    planned_immunizations: Optional[Sequence[PlannedImmunizationProtocol]] = None,
    instructions: Optional[Sequence[InstructionProtocol]] = None,
    title: str = "Plan of Treatment",
    version: CDAVersion = CDAVersion.R2_1,
    **kwargs,
):
    """
    Initialize PlanOfTreatmentSection builder.

    Args:
        planned_observations: List of planned observations
        planned_procedures: List of planned procedures
        planned_encounters: List of planned encounters
        planned_acts: List of planned acts
        planned_medications: List of planned medications
        planned_supplies: List of planned supplies
        planned_immunizations: List of planned immunizations
        instructions: List of instructions
        title: Section title (default: "Plan of Treatment")
        version: C-CDA version (R2.1 or R2.0)
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.planned_observations = planned_observations or []
    self.planned_procedures = planned_procedures or []
    self.planned_encounters = planned_encounters or []
    self.planned_acts = planned_acts or []
    self.planned_medications = planned_medications or []
    self.planned_supplies = planned_supplies or []
    self.planned_immunizations = planned_immunizations or []
    self.instructions = instructions or []
    self.title = title

build()

Build Plan of Treatment Section XML element.

Returns:

Type Description
Element

lxml Element for section

Source code in ccdakit/builders/sections/plan_of_treatment.py
def build(self) -> etree.Element:
    """
    Build Plan of Treatment Section XML element.

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add template IDs (CONF:1098-7723, CONF:1098-10435, CONF:1098-32501)
    self.add_template_ids(section)

    # Add section code (CONF:1098-14749, CONF:1098-14750, CONF:1098-30813)
    code_elem = Code(
        code="18776-5",
        system="LOINC",
        display_name="Plan of Treatment",
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # Add title (CONF:1098-16986)
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (CONF:1098-7725)
    self._add_narrative(section)

    # Add entries for each type of planned activity
    # Planned Observations (CONF:1098-7726, CONF:1098-14751)
    for obs in self.planned_observations:
        self._add_entry(section, obs, PlannedObservation)

    # Planned Encounters (CONF:1098-8805, CONF:1098-30472)
    for enc in self.planned_encounters:
        self._add_entry(section, enc, PlannedEncounter)

    # Planned Acts (CONF:1098-8807, CONF:1098-30473)
    for act in self.planned_acts:
        self._add_entry(section, act, PlannedAct)

    # Planned Procedures (CONF:1098-8809, CONF:1098-30474)
    for proc in self.planned_procedures:
        self._add_entry(section, proc, PlannedProcedure)

    # Planned Medications (CONF:1098-8811, CONF:1098-30475)
    for med in self.planned_medications:
        self._add_entry(section, med, PlannedMedication)

    # Planned Supplies (CONF:1098-8813, CONF:1098-30476)
    for supply in self.planned_supplies:
        self._add_entry(section, supply, PlannedSupply)

    # Instructions (CONF:1098-14695, CONF:1098-31397)
    for instruction in self.instructions:
        self._add_entry(section, instruction, Instruction)

    # Planned Immunizations (CONF:1098-32353, CONF:1098-32354)
    for immunization in self.planned_immunizations:
        self._add_entry(section, immunization, PlannedImmunization)

    return section

PostoperativeDiagnosisSection

PostoperativeDiagnosisSection

Bases: CDAElement

Builder for C-CDA Postoperative Diagnosis Section.

The Postoperative Diagnosis Section records the diagnosis or diagnoses discovered or confirmed during surgery. Often it is the same as the preoperative diagnosis.

This is a simple narrative-only section without structured entries. Supports both R2.1 and R2.0 versions.

Conformance: - Template ID: 2.16.840.1.113883.10.20.22.2.35 - Code: 10218-6 (Postoperative Diagnosis) from LOINC - Contains: Only narrative text, no structured entries

Source code in ccdakit/builders/sections/postoperative_diagnosis.py
class PostoperativeDiagnosisSection(CDAElement):
    """
    Builder for C-CDA Postoperative Diagnosis Section.

    The Postoperative Diagnosis Section records the diagnosis or diagnoses
    discovered or confirmed during surgery. Often it is the same as the
    preoperative diagnosis.

    This is a simple narrative-only section without structured entries.
    Supports both R2.1 and R2.0 versions.

    Conformance:
    - Template ID: 2.16.840.1.113883.10.20.22.2.35
    - Code: 10218-6 (Postoperative Diagnosis) from LOINC
    - Contains: Only narrative text, no structured entries
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.35",
                extension=None,  # No extension specified for this section
                description="Postoperative Diagnosis Section",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.35",
                extension=None,
                description="Postoperative Diagnosis Section",
            ),
        ],
    }

    def __init__(
        self,
        diagnosis_text: str,
        title: str = "Postoperative Diagnosis",
        version: CDAVersion = CDAVersion.R2_1,
        **kwargs,
    ):
        """
        Initialize PostoperativeDiagnosisSection builder.

        Args:
            diagnosis_text: The narrative text describing the postoperative diagnosis
            title: Section title (default: "Postoperative Diagnosis")
            version: C-CDA version (R2.1 or R2.0)
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.diagnosis_text = diagnosis_text
        self.title = title

    def build(self) -> etree.Element:
        """
        Build Postoperative Diagnosis Section XML element.

        Conformance:
        - CONF:81-8101: SHALL contain exactly one [1..1] templateId
        - CONF:81-10437: templateId/@root="2.16.840.1.113883.10.20.22.2.35"
        - CONF:81-15401: SHALL contain exactly one [1..1] code
        - CONF:81-15402: code/@code="10218-6" (Postoperative Diagnosis)
        - CONF:81-26488: code/@codeSystem="2.16.840.1.113883.6.1" (LOINC)
        - CONF:81-8103: SHALL contain exactly one [1..1] title
        - CONF:81-8104: SHALL contain exactly one [1..1] text

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add template IDs (CONF:81-8101, CONF:81-10437)
        self.add_template_ids(section)

        # Add section code (CONF:81-15401, CONF:81-15402, CONF:81-26488)
        # 10218-6 = Postoperative Diagnosis (LOINC)
        code_elem = Code(
            code="10218-6",
            system="LOINC",
            display_name="Postoperative Diagnosis",
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # Add title (CONF:81-8103)
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (CONF:81-8104)
        self._add_narrative(section)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element.

        The narrative provides human-readable content for the section.
        For Postoperative Diagnosis, this typically contains the diagnosis
        or diagnoses discovered or confirmed during surgery.

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        # Add diagnosis text as paragraph
        paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
        paragraph.text = self.diagnosis_text

Functions

__init__(diagnosis_text, title='Postoperative Diagnosis', version=CDAVersion.R2_1, **kwargs)

Initialize PostoperativeDiagnosisSection builder.

Parameters:

Name Type Description Default
diagnosis_text str

The narrative text describing the postoperative diagnosis

required
title str

Section title (default: "Postoperative Diagnosis")

'Postoperative Diagnosis'
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/postoperative_diagnosis.py
def __init__(
    self,
    diagnosis_text: str,
    title: str = "Postoperative Diagnosis",
    version: CDAVersion = CDAVersion.R2_1,
    **kwargs,
):
    """
    Initialize PostoperativeDiagnosisSection builder.

    Args:
        diagnosis_text: The narrative text describing the postoperative diagnosis
        title: Section title (default: "Postoperative Diagnosis")
        version: C-CDA version (R2.1 or R2.0)
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.diagnosis_text = diagnosis_text
    self.title = title

build()

Build Postoperative Diagnosis Section XML element.

Conformance: - CONF:81-8101: SHALL contain exactly one [1..1] templateId - CONF:81-10437: templateId/@root="2.16.840.1.113883.10.20.22.2.35" - CONF:81-15401: SHALL contain exactly one [1..1] code - CONF:81-15402: code/@code="10218-6" (Postoperative Diagnosis) - CONF:81-26488: code/@codeSystem="2.16.840.1.113883.6.1" (LOINC) - CONF:81-8103: SHALL contain exactly one [1..1] title - CONF:81-8104: SHALL contain exactly one [1..1] text

Returns:

Type Description
Element

lxml Element for section

Source code in ccdakit/builders/sections/postoperative_diagnosis.py
def build(self) -> etree.Element:
    """
    Build Postoperative Diagnosis Section XML element.

    Conformance:
    - CONF:81-8101: SHALL contain exactly one [1..1] templateId
    - CONF:81-10437: templateId/@root="2.16.840.1.113883.10.20.22.2.35"
    - CONF:81-15401: SHALL contain exactly one [1..1] code
    - CONF:81-15402: code/@code="10218-6" (Postoperative Diagnosis)
    - CONF:81-26488: code/@codeSystem="2.16.840.1.113883.6.1" (LOINC)
    - CONF:81-8103: SHALL contain exactly one [1..1] title
    - CONF:81-8104: SHALL contain exactly one [1..1] text

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add template IDs (CONF:81-8101, CONF:81-10437)
    self.add_template_ids(section)

    # Add section code (CONF:81-15401, CONF:81-15402, CONF:81-26488)
    # 10218-6 = Postoperative Diagnosis (LOINC)
    code_elem = Code(
        code="10218-6",
        system="LOINC",
        display_name="Postoperative Diagnosis",
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # Add title (CONF:81-8103)
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (CONF:81-8104)
    self._add_narrative(section)

    return section

PreoperativeDiagnosisSection

PreoperativeDiagnosisSection

Bases: CDAElement

Builder for C-CDA Preoperative Diagnosis Section.

The Preoperative Diagnosis Section records the surgical diagnoses assigned to the patient before the surgical procedure which are the reason for the surgery. The preoperative diagnosis is, in the surgeon's opinion, the diagnosis that will be confirmed during surgery.

Includes narrative (HTML table) and structured entries. Supports R2.1 (2015-08-01) version.

Template ID: 2.16.840.1.113883.10.20.22.2.34

Source code in ccdakit/builders/sections/preoperative_diagnosis.py
class PreoperativeDiagnosisSection(CDAElement):
    """
    Builder for C-CDA Preoperative Diagnosis Section.

    The Preoperative Diagnosis Section records the surgical diagnoses assigned
    to the patient before the surgical procedure which are the reason for the
    surgery. The preoperative diagnosis is, in the surgeon's opinion, the
    diagnosis that will be confirmed during surgery.

    Includes narrative (HTML table) and structured entries.
    Supports R2.1 (2015-08-01) version.

    Template ID: 2.16.840.1.113883.10.20.22.2.34
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.34",
                extension="2015-08-01",
                description="Preoperative Diagnosis Section R2.1",
            ),
        ],
    }

    def __init__(
        self,
        diagnoses: Sequence[PreoperativeDiagnosisProtocol],
        title: str = "Preoperative Diagnosis",
        version: CDAVersion = CDAVersion.R2_1,
        **kwargs,
    ):
        """
        Initialize PreoperativeDiagnosisSection builder.

        Args:
            diagnoses: List of preoperative diagnoses satisfying PreoperativeDiagnosisProtocol
            title: Section title (default: "Preoperative Diagnosis")
            version: C-CDA version (R2.1)
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.diagnoses = diagnoses
        self.title = title

    def build(self) -> etree.Element:
        """
        Build Preoperative Diagnosis Section XML element.

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add template IDs (CONF:1198-8097, CONF:1198-10439, CONF:1198-32551)
        self.add_template_ids(section)

        # Add section code (CONF:1198-15405, CONF:1198-15406, CONF:1198-30863)
        code_elem = Code(
            code="10219-4",
            system="LOINC",
            display_name="Preoperative Diagnosis",
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # Add title (CONF:1198-8099)
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (HTML table) (CONF:1198-8100)
        self._add_narrative(section)

        # Add entries with Preoperative Diagnosis Acts (CONF:1198-10096, CONF:1198-15504)
        for diagnosis in self.diagnoses:
            self._add_entry(section, diagnosis)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element with HTML table.

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        if not self.diagnoses:
            # No diagnoses - add "No preoperative diagnosis" paragraph
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            paragraph.text = "No preoperative diagnosis"
            return

        # Create table for diagnoses
        table = etree.SubElement(text, f"{{{NS}}}table", border="1", width="100%")

        # Table header
        thead = etree.SubElement(table, f"{{{NS}}}thead")
        tr = etree.SubElement(thead, f"{{{NS}}}tr")

        headers = ["Diagnosis", "Code", "Status", "Diagnosis Date"]
        for header_text in headers:
            th = etree.SubElement(tr, f"{{{NS}}}th")
            th.text = header_text

        # Table body
        tbody = etree.SubElement(table, f"{{{NS}}}tbody")

        for idx, diagnosis in enumerate(self.diagnoses, start=1):
            tr = etree.SubElement(tbody, f"{{{NS}}}tr")

            # Diagnosis name (with ID reference)
            td_name = etree.SubElement(tr, f"{{{NS}}}td")
            content = etree.SubElement(
                td_name,
                f"{{{NS}}}content",
                ID=f"preop-diagnosis-{idx}",
            )
            content.text = diagnosis.name

            # Code
            td_code = etree.SubElement(tr, f"{{{NS}}}td")
            td_code.text = f"{diagnosis.code} ({diagnosis.code_system})"

            # Status
            td_status = etree.SubElement(tr, f"{{{NS}}}td")
            td_status.text = diagnosis.status.capitalize()

            # Diagnosis date
            td_date = etree.SubElement(tr, f"{{{NS}}}td")
            if diagnosis.diagnosis_date:
                td_date.text = diagnosis.diagnosis_date.strftime("%Y-%m-%d")
            else:
                td_date.text = "Unknown"

    def _add_entry(self, section: etree._Element, diagnosis: PreoperativeDiagnosisProtocol) -> None:
        """
        Add entry element with Preoperative Diagnosis Act.

        Args:
            section: section element
            diagnosis: Preoperative diagnosis data
        """
        # Create entry element
        entry = etree.SubElement(section, f"{{{NS}}}entry")

        # Create and add Preoperative Diagnosis Act
        act_builder = PreoperativeDiagnosisEntry(diagnosis, version=self.version)
        entry.append(act_builder.to_element())

Functions

__init__(diagnoses, title='Preoperative Diagnosis', version=CDAVersion.R2_1, **kwargs)

Initialize PreoperativeDiagnosisSection builder.

Parameters:

Name Type Description Default
diagnoses Sequence[PreoperativeDiagnosisProtocol]

List of preoperative diagnoses satisfying PreoperativeDiagnosisProtocol

required
title str

Section title (default: "Preoperative Diagnosis")

'Preoperative Diagnosis'
version CDAVersion

C-CDA version (R2.1)

R2_1
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/preoperative_diagnosis.py
def __init__(
    self,
    diagnoses: Sequence[PreoperativeDiagnosisProtocol],
    title: str = "Preoperative Diagnosis",
    version: CDAVersion = CDAVersion.R2_1,
    **kwargs,
):
    """
    Initialize PreoperativeDiagnosisSection builder.

    Args:
        diagnoses: List of preoperative diagnoses satisfying PreoperativeDiagnosisProtocol
        title: Section title (default: "Preoperative Diagnosis")
        version: C-CDA version (R2.1)
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.diagnoses = diagnoses
    self.title = title

build()

Build Preoperative Diagnosis Section XML element.

Returns:

Type Description
Element

lxml Element for section

Source code in ccdakit/builders/sections/preoperative_diagnosis.py
def build(self) -> etree.Element:
    """
    Build Preoperative Diagnosis Section XML element.

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add template IDs (CONF:1198-8097, CONF:1198-10439, CONF:1198-32551)
    self.add_template_ids(section)

    # Add section code (CONF:1198-15405, CONF:1198-15406, CONF:1198-30863)
    code_elem = Code(
        code="10219-4",
        system="LOINC",
        display_name="Preoperative Diagnosis",
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # Add title (CONF:1198-8099)
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (HTML table) (CONF:1198-8100)
    self._add_narrative(section)

    # Add entries with Preoperative Diagnosis Acts (CONF:1198-10096, CONF:1198-15504)
    for diagnosis in self.diagnoses:
        self._add_entry(section, diagnosis)

    return section

ProblemsSection

ProblemsSection

Bases: CDAElement

Builder for C-CDA Problems Section.

Includes narrative (HTML table) and structured entries. Supports both R2.1 (2015-08-01) and R2.0 (2014-06-09) versions.

Functions

__init__(problems, title='Problems', version=CDAVersion.R2_1, **kwargs)

Initialize ProblemsSection builder.

Parameters:

Name Type Description Default
problems Sequence[ProblemProtocol]

List of problems satisfying ProblemProtocol

required
title str

Section title (default: "Problems")

'Problems'
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
**kwargs

Additional arguments passed to CDAElement

{}

build()

Build Problems Section XML element.

Returns:

Type Description
Element

lxml Element for section

ProceduresSection

ProceduresSection

Bases: CDAElement

Builder for C-CDA Procedures Section.

Includes narrative (HTML table) and structured entries. Supports both R2.1 (2015-08-01) and R2.0 (2014-06-09) versions.

Source code in ccdakit/builders/sections/procedures.py
class ProceduresSection(CDAElement):
    """
    Builder for C-CDA Procedures Section.

    Includes narrative (HTML table) and structured entries.
    Supports both R2.1 (2015-08-01) and R2.0 (2014-06-09) versions.
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.7.1",
                extension="2015-08-01",
                description="Procedures Section (entries required) R2.1",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.7.1",
                extension="2014-06-09",
                description="Procedures Section (entries required) R2.0",
            ),
        ],
    }

    def __init__(
        self,
        procedures: Sequence[ProcedureProtocol],
        title: str = "Procedures",
        version: CDAVersion = CDAVersion.R2_1,
        **kwargs,
    ):
        """
        Initialize ProceduresSection builder.

        Args:
            procedures: List of procedures satisfying ProcedureProtocol
            title: Section title (default: "Procedures")
            version: C-CDA version (R2.1 or R2.0)
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.procedures = procedures
        self.title = title

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

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add template IDs
        self.add_template_ids(section)

        # Add section code (47519-4 = History of Procedures)
        code_elem = Code(
            code="47519-4",
            system="LOINC",
            display_name="History of Procedures",
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # Add title
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (HTML table)
        self._add_narrative(section)

        # Add entries with Procedure Activities
        for procedure in self.procedures:
            self._add_entry(section, procedure)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element with HTML table.

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        if not self.procedures:
            # No procedures - add "No procedures recorded" paragraph
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            paragraph.text = "No procedures recorded"
            return

        # Create table for procedures
        table = etree.SubElement(text, f"{{{NS}}}table", border="1", width="100%")

        # Table header
        thead = etree.SubElement(table, f"{{{NS}}}thead")
        tr = etree.SubElement(thead, f"{{{NS}}}tr")

        headers = [
            "Procedure",
            "Code",
            "Date",
            "Status",
            "Target Site",
            "Performer",
        ]
        for header_text in headers:
            th = etree.SubElement(tr, f"{{{NS}}}th")
            th.text = header_text

        # Table body
        tbody = etree.SubElement(table, f"{{{NS}}}tbody")

        for idx, procedure in enumerate(self.procedures, start=1):
            tr = etree.SubElement(tbody, f"{{{NS}}}tr")

            # Procedure name (with ID reference)
            td_name = etree.SubElement(tr, f"{{{NS}}}td")
            content = etree.SubElement(
                td_name,
                f"{{{NS}}}content",
                ID=f"procedure-{idx}",
            )
            content.text = procedure.name

            # Code
            td_code = etree.SubElement(tr, f"{{{NS}}}td")
            td_code.text = f"{procedure.code} ({procedure.code_system})"

            # Date
            td_date = etree.SubElement(tr, f"{{{NS}}}td")
            if procedure.date:
                # Format based on whether it's a date or datetime
                if hasattr(procedure.date, "hour"):
                    # It's a datetime
                    td_date.text = procedure.date.strftime("%Y-%m-%d %H:%M")
                else:
                    # It's a date
                    td_date.text = procedure.date.strftime("%Y-%m-%d")
            else:
                td_date.text = "Unknown"

            # Status
            td_status = etree.SubElement(tr, f"{{{NS}}}td")
            td_status.text = procedure.status.capitalize()

            # Target site
            td_site = etree.SubElement(tr, f"{{{NS}}}td")
            if procedure.target_site:
                td_site.text = procedure.target_site
            else:
                td_site.text = "-"

            # Performer
            td_performer = etree.SubElement(tr, f"{{{NS}}}td")
            if procedure.performer_name:
                td_performer.text = procedure.performer_name
            else:
                td_performer.text = "-"

    def _add_entry(self, section: etree._Element, procedure: ProcedureProtocol) -> None:
        """
        Add entry element with Procedure Activity.

        Args:
            section: section element
            procedure: Procedure data
        """
        # Create entry element
        entry = etree.SubElement(section, f"{{{NS}}}entry", typeCode="DRIV")

        # Create and add Procedure Activity
        proc_builder = ProcedureActivity(procedure, version=self.version)
        entry.append(proc_builder.to_element())

Functions

__init__(procedures, title='Procedures', version=CDAVersion.R2_1, **kwargs)

Initialize ProceduresSection builder.

Parameters:

Name Type Description Default
procedures Sequence[ProcedureProtocol]

List of procedures satisfying ProcedureProtocol

required
title str

Section title (default: "Procedures")

'Procedures'
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/procedures.py
def __init__(
    self,
    procedures: Sequence[ProcedureProtocol],
    title: str = "Procedures",
    version: CDAVersion = CDAVersion.R2_1,
    **kwargs,
):
    """
    Initialize ProceduresSection builder.

    Args:
        procedures: List of procedures satisfying ProcedureProtocol
        title: Section title (default: "Procedures")
        version: C-CDA version (R2.1 or R2.0)
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.procedures = procedures
    self.title = title

build()

Build Procedures Section XML element.

Returns:

Type Description
Element

lxml Element for section

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

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add template IDs
    self.add_template_ids(section)

    # Add section code (47519-4 = History of Procedures)
    code_elem = Code(
        code="47519-4",
        system="LOINC",
        display_name="History of Procedures",
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # Add title
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (HTML table)
    self._add_narrative(section)

    # Add entries with Procedure Activities
    for procedure in self.procedures:
        self._add_entry(section, procedure)

    return section

ReasonForVisitSection

ReasonForVisitSection

Bases: CDAElement

Builder for C-CDA Reason for Visit Section.

This section records the patient's reason for the patient's visit as documented by the provider. Local policy determines whether Reason for Visit and Chief Complaint are in separate or combined sections.

This is a simple narrative-only section without structured entries. Supports both R2.1 and R2.0 versions.

Conformance: - Template ID: 2.16.840.1.113883.10.20.22.2.12 - Code: 29299-5 (Reason for Visit) from LOINC - Contains: Only narrative text, no structured entries

Source code in ccdakit/builders/sections/reason_for_visit.py
class ReasonForVisitSection(CDAElement):
    """
    Builder for C-CDA Reason for Visit Section.

    This section records the patient's reason for the patient's visit as documented
    by the provider. Local policy determines whether Reason for Visit and Chief
    Complaint are in separate or combined sections.

    This is a simple narrative-only section without structured entries.
    Supports both R2.1 and R2.0 versions.

    Conformance:
    - Template ID: 2.16.840.1.113883.10.20.22.2.12
    - Code: 29299-5 (Reason for Visit) from LOINC
    - Contains: Only narrative text, no structured entries
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.12",
                extension=None,  # R2.1 does not specify extension for this section
                description="Reason for Visit Section R2.1",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.12",
                extension=None,
                description="Reason for Visit Section R2.0",
            ),
        ],
    }

    def __init__(
        self,
        reason_text: str,
        title: str = "Reason for Visit",
        version: CDAVersion = CDAVersion.R2_1,
        **kwargs,
    ):
        """
        Initialize ReasonForVisitSection builder.

        Args:
            reason_text: The narrative text describing the reason for visit
            title: Section title (default: "Reason for Visit")
            version: C-CDA version (R2.1 or R2.0)
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.reason_text = reason_text
        self.title = title

    def build(self) -> etree.Element:
        """
        Build Reason for Visit Section XML element.

        Conformance:
        - CONF:81-7836: SHALL contain exactly one [1..1] templateId
        - CONF:81-10448: templateId/@root="2.16.840.1.113883.10.20.22.2.12"
        - CONF:81-15429: SHALL contain exactly one [1..1] code
        - CONF:81-15430: code/@code="29299-5" (Reason for Visit)
        - CONF:81-26494: code/@codeSystem="2.16.840.1.113883.6.1" (LOINC)
        - CONF:81-7838: SHALL contain exactly one [1..1] title
        - CONF:81-7839: SHALL contain exactly one [1..1] text

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add template IDs (CONF:81-7836, CONF:81-10448)
        self.add_template_ids(section)

        # Add section code (CONF:81-15429, CONF:81-15430, CONF:81-26494)
        # 29299-5 = Reason for Visit (LOINC)
        code_elem = Code(
            code="29299-5",
            system="LOINC",
            display_name="Reason for Visit",
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # Add title (CONF:81-7838)
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (CONF:81-7839)
        self._add_narrative(section)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element.

        The narrative provides human-readable content for the section.
        For Reason for Visit, this is typically a simple paragraph containing
        the provider's documentation of why the patient is seeking care.

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        # Add reason text as paragraph
        paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
        paragraph.text = self.reason_text

Functions

__init__(reason_text, title='Reason for Visit', version=CDAVersion.R2_1, **kwargs)

Initialize ReasonForVisitSection builder.

Parameters:

Name Type Description Default
reason_text str

The narrative text describing the reason for visit

required
title str

Section title (default: "Reason for Visit")

'Reason for Visit'
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/reason_for_visit.py
def __init__(
    self,
    reason_text: str,
    title: str = "Reason for Visit",
    version: CDAVersion = CDAVersion.R2_1,
    **kwargs,
):
    """
    Initialize ReasonForVisitSection builder.

    Args:
        reason_text: The narrative text describing the reason for visit
        title: Section title (default: "Reason for Visit")
        version: C-CDA version (R2.1 or R2.0)
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.reason_text = reason_text
    self.title = title

build()

Build Reason for Visit Section XML element.

Conformance: - CONF:81-7836: SHALL contain exactly one [1..1] templateId - CONF:81-10448: templateId/@root="2.16.840.1.113883.10.20.22.2.12" - CONF:81-15429: SHALL contain exactly one [1..1] code - CONF:81-15430: code/@code="29299-5" (Reason for Visit) - CONF:81-26494: code/@codeSystem="2.16.840.1.113883.6.1" (LOINC) - CONF:81-7838: SHALL contain exactly one [1..1] title - CONF:81-7839: SHALL contain exactly one [1..1] text

Returns:

Type Description
Element

lxml Element for section

Source code in ccdakit/builders/sections/reason_for_visit.py
def build(self) -> etree.Element:
    """
    Build Reason for Visit Section XML element.

    Conformance:
    - CONF:81-7836: SHALL contain exactly one [1..1] templateId
    - CONF:81-10448: templateId/@root="2.16.840.1.113883.10.20.22.2.12"
    - CONF:81-15429: SHALL contain exactly one [1..1] code
    - CONF:81-15430: code/@code="29299-5" (Reason for Visit)
    - CONF:81-26494: code/@codeSystem="2.16.840.1.113883.6.1" (LOINC)
    - CONF:81-7838: SHALL contain exactly one [1..1] title
    - CONF:81-7839: SHALL contain exactly one [1..1] text

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add template IDs (CONF:81-7836, CONF:81-10448)
    self.add_template_ids(section)

    # Add section code (CONF:81-15429, CONF:81-15430, CONF:81-26494)
    # 29299-5 = Reason for Visit (LOINC)
    code_elem = Code(
        code="29299-5",
        system="LOINC",
        display_name="Reason for Visit",
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # Add title (CONF:81-7838)
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (CONF:81-7839)
    self._add_narrative(section)

    return section

ResultsSection

ResultsSection

Bases: CDAElement

Builder for C-CDA Results Section (entries required).

Includes narrative (HTML table) and structured entries for lab results. Implements Results Section V3 (template 2.16.840.1.113883.10.20.22.2.3.1:2015-08-01).

Key features: - Groups lab results by panel/organizer - Displays test name, value, unit, interpretation, and reference range - Supports LOINC codes for test identification

Source code in ccdakit/builders/sections/results.py
class ResultsSection(CDAElement):
    """
    Builder for C-CDA Results Section (entries required).

    Includes narrative (HTML table) and structured entries for lab results.
    Implements Results Section V3 (template 2.16.840.1.113883.10.20.22.2.3.1:2015-08-01).

    Key features:
    - Groups lab results by panel/organizer
    - Displays test name, value, unit, interpretation, and reference range
    - Supports LOINC codes for test identification
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.3.1",
                extension="2015-08-01",
                description="Results Section (entries required) V3",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.3.1",
                extension="2015-08-01",
                description="Results Section (entries required) V3",
            ),
        ],
    }

    def __init__(
        self,
        result_organizers: Sequence[ResultOrganizerProtocol],
        title: str = "Results",
        version: CDAVersion = CDAVersion.R2_1,
        **kwargs,
    ):
        """
        Initialize ResultsSection builder.

        Args:
            result_organizers: List of result organizers (lab panels)
            title: Section title (default: "Results")
            version: C-CDA version (R2.1 or R2.0)
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.result_organizers = result_organizers
        self.title = title

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

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add template IDs
        self.add_template_ids(section)

        # Add section code (30954-2 = Relevant diagnostic tests and/or laboratory data)
        code_elem = Code(
            code="30954-2",
            system="LOINC",
            display_name="Relevant diagnostic tests and/or laboratory data",
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # Add title
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (HTML table)
        self._add_narrative(section)

        # Add entries with Result Organizers
        for organizer in self.result_organizers:
            self._add_entry(section, organizer)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element with HTML table.

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        if not self.result_organizers:
            # No results - add "No lab results available" paragraph
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            paragraph.text = "No lab results available"
            return

        # Create table for results
        table = etree.SubElement(text, f"{{{NS}}}table", border="1", width="100%")

        # Table header
        thead = etree.SubElement(table, f"{{{NS}}}thead")
        tr = etree.SubElement(thead, f"{{{NS}}}tr")

        headers = [
            "Panel",
            "Test",
            "Value",
            "Unit",
            "Interpretation",
            "Reference Range",
            "Date",
        ]
        for header_text in headers:
            th = etree.SubElement(tr, f"{{{NS}}}th")
            th.text = header_text

        # Table body
        tbody = etree.SubElement(table, f"{{{NS}}}tbody")

        for organizer_idx, organizer in enumerate(self.result_organizers, start=1):
            # Add each result in the organizer
            for result_idx, result in enumerate(organizer.results, start=1):
                tr = etree.SubElement(tbody, f"{{{NS}}}tr")

                # Panel name (only on first row of each panel)
                td_panel = etree.SubElement(tr, f"{{{NS}}}td")
                if result_idx == 1:
                    content_panel = etree.SubElement(
                        td_panel,
                        f"{{{NS}}}content",
                        ID=f"result-panel-{organizer_idx}",
                    )
                    content_panel.text = organizer.panel_name
                else:
                    td_panel.text = ""  # Empty for subsequent rows

                # Test name (with ID reference)
                td_test = etree.SubElement(tr, f"{{{NS}}}td")
                content_test = etree.SubElement(
                    td_test,
                    f"{{{NS}}}content",
                    ID=f"result-{organizer_idx}-{result_idx}",
                )
                content_test.text = result.test_name

                # Value
                td_value = etree.SubElement(tr, f"{{{NS}}}td")
                td_value.text = result.value

                # Unit
                td_unit = etree.SubElement(tr, f"{{{NS}}}td")
                if result.unit:
                    td_unit.text = result.unit
                else:
                    td_unit.text = "-"

                # Interpretation
                td_interpretation = etree.SubElement(tr, f"{{{NS}}}td")
                if result.interpretation:
                    td_interpretation.text = result.interpretation
                else:
                    td_interpretation.text = "-"

                # Reference Range
                td_range = etree.SubElement(tr, f"{{{NS}}}td")
                if result.reference_range_low or result.reference_range_high:
                    range_text = []
                    if result.reference_range_low:
                        range_text.append(result.reference_range_low)
                    if result.reference_range_high:
                        if range_text:
                            range_text.append(f" - {result.reference_range_high}")
                        else:
                            range_text.append(f"< {result.reference_range_high}")
                    if result.reference_range_unit:
                        range_text.append(f" {result.reference_range_unit}")
                    td_range.text = "".join(range_text)
                else:
                    td_range.text = "-"

                # Date (from organizer or result)
                td_date = etree.SubElement(tr, f"{{{NS}}}td")
                # Use organizer date for consistency
                td_date.text = organizer.effective_time.strftime("%Y-%m-%d")

    def _add_entry(self, section: etree._Element, organizer: ResultOrganizerProtocol) -> None:
        """
        Add entry element with Result Organizer.

        Args:
            section: section element
            organizer: Result organizer data
        """
        # Create entry element
        entry = etree.SubElement(section, f"{{{NS}}}entry", typeCode="DRIV")

        # Create and add Result Organizer
        organizer_builder = ResultOrganizer(organizer, version=self.version)
        entry.append(organizer_builder.to_element())

Functions

__init__(result_organizers, title='Results', version=CDAVersion.R2_1, **kwargs)

Initialize ResultsSection builder.

Parameters:

Name Type Description Default
result_organizers Sequence[ResultOrganizerProtocol]

List of result organizers (lab panels)

required
title str

Section title (default: "Results")

'Results'
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/results.py
def __init__(
    self,
    result_organizers: Sequence[ResultOrganizerProtocol],
    title: str = "Results",
    version: CDAVersion = CDAVersion.R2_1,
    **kwargs,
):
    """
    Initialize ResultsSection builder.

    Args:
        result_organizers: List of result organizers (lab panels)
        title: Section title (default: "Results")
        version: C-CDA version (R2.1 or R2.0)
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.result_organizers = result_organizers
    self.title = title

build()

Build Results Section XML element.

Returns:

Type Description
Element

lxml Element for section

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

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add template IDs
    self.add_template_ids(section)

    # Add section code (30954-2 = Relevant diagnostic tests and/or laboratory data)
    code_elem = Code(
        code="30954-2",
        system="LOINC",
        display_name="Relevant diagnostic tests and/or laboratory data",
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # Add title
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (HTML table)
    self._add_narrative(section)

    # Add entries with Result Organizers
    for organizer in self.result_organizers:
        self._add_entry(section, organizer)

    return section

SocialHistorySection

SocialHistorySection

Bases: CDAElement

Builder for C-CDA Social History Section.

Contains social history data that influence a patient's physical, psychological or emotional health (e.g., smoking status, pregnancy). This implementation focuses on smoking status observations as specified in Meaningful Use requirements.

Template: 2.16.840.1.113883.10.20.22.2.17 (V3: 2015-08-01) Code: 29762-2 (Social History) from LOINC

Includes narrative (HTML table) and structured entries. Supports both R2.1 (2015-08-01) and R2.0 (2015-08-01) versions.

Source code in ccdakit/builders/sections/social_history.py
class SocialHistorySection(CDAElement):
    """
    Builder for C-CDA Social History Section.

    Contains social history data that influence a patient's physical, psychological
    or emotional health (e.g., smoking status, pregnancy). This implementation focuses
    on smoking status observations as specified in Meaningful Use requirements.

    Template: 2.16.840.1.113883.10.20.22.2.17 (V3: 2015-08-01)
    Code: 29762-2 (Social History) from LOINC

    Includes narrative (HTML table) and structured entries.
    Supports both R2.1 (2015-08-01) and R2.0 (2015-08-01) versions.
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.17",
                extension="2015-08-01",
                description="Social History Section (V3) R2.1",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.17",
                extension="2015-08-01",
                description="Social History Section (V3) R2.0",
            ),
        ],
    }

    # LOINC code for social history section
    SOCIAL_HISTORY_CODE = "29762-2"
    SOCIAL_HISTORY_DISPLAY = "Social History"

    def __init__(
        self,
        smoking_statuses: Sequence[SmokingStatusProtocol],
        title: str = "Social History",
        version: CDAVersion = CDAVersion.R2_1,
        **kwargs,
    ):
        """
        Initialize SocialHistorySection builder.

        Args:
            smoking_statuses: List of smoking status observations
            title: Section title (default: "Social History")
            version: C-CDA version (R2.1 or R2.0)
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.smoking_statuses = smoking_statuses
        self.title = title

    def build(self) -> etree.Element:
        """
        Build Social History Section XML element.

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add template IDs
        self.add_template_ids(section)

        # Add section code (29762-2 = Social History)
        code_elem = Code(
            code=self.SOCIAL_HISTORY_CODE,
            system="LOINC",
            display_name=self.SOCIAL_HISTORY_DISPLAY,
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # Add title
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (HTML table)
        self._add_narrative(section)

        # Add entries with Smoking Status Observations
        for status in self.smoking_statuses:
            self._add_entry(section, status)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element with HTML table.

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        if not self.smoking_statuses:
            # No smoking status - add "No social history information available" paragraph
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            paragraph.text = "No social history information available"
            return

        # Create table for smoking status
        table = etree.SubElement(text, f"{{{NS}}}table", border="1", width="100%")

        # Table header
        thead = etree.SubElement(table, f"{{{NS}}}thead")
        tr = etree.SubElement(thead, f"{{{NS}}}tr")

        headers = [
            "Social History Type",
            "Status",
            "Date Observed",
        ]
        for header_text in headers:
            th = etree.SubElement(tr, f"{{{NS}}}th")
            th.text = header_text

        # Table body
        tbody = etree.SubElement(table, f"{{{NS}}}tbody")

        for idx, status in enumerate(self.smoking_statuses, start=1):
            tr = etree.SubElement(tbody, f"{{{NS}}}tr")

            # Social History Type
            td_type = etree.SubElement(tr, f"{{{NS}}}td")
            td_type.text = "Smoking Status"

            # Status (with ID reference)
            td_status = etree.SubElement(tr, f"{{{NS}}}td")
            content = etree.SubElement(
                td_status,
                f"{{{NS}}}content",
                ID=f"smoking-status-{idx}",
            )
            content.text = status.smoking_status

            # Date Observed
            td_date = etree.SubElement(tr, f"{{{NS}}}td")
            # Format date/datetime appropriately
            if hasattr(status.date, "strftime"):
                if hasattr(status.date, "hour"):
                    # It's a datetime
                    td_date.text = status.date.strftime("%Y-%m-%d %H:%M")
                else:
                    # It's a date
                    td_date.text = status.date.strftime("%Y-%m-%d")
            else:
                td_date.text = str(status.date)

    def _add_entry(self, section: etree._Element, status: SmokingStatusProtocol) -> None:
        """
        Add entry element with Smoking Status Observation.

        Args:
            section: section element
            status: Smoking status data
        """
        # Create entry element
        entry = etree.SubElement(section, f"{{{NS}}}entry")

        # Create and add Smoking Status Observation
        obs_builder = SmokingStatusObservation(status, version=self.version)
        entry.append(obs_builder.to_element())

Functions

__init__(smoking_statuses, title='Social History', version=CDAVersion.R2_1, **kwargs)

Initialize SocialHistorySection builder.

Parameters:

Name Type Description Default
smoking_statuses Sequence[SmokingStatusProtocol]

List of smoking status observations

required
title str

Section title (default: "Social History")

'Social History'
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/social_history.py
def __init__(
    self,
    smoking_statuses: Sequence[SmokingStatusProtocol],
    title: str = "Social History",
    version: CDAVersion = CDAVersion.R2_1,
    **kwargs,
):
    """
    Initialize SocialHistorySection builder.

    Args:
        smoking_statuses: List of smoking status observations
        title: Section title (default: "Social History")
        version: C-CDA version (R2.1 or R2.0)
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.smoking_statuses = smoking_statuses
    self.title = title

build()

Build Social History Section XML element.

Returns:

Type Description
Element

lxml Element for section

Source code in ccdakit/builders/sections/social_history.py
def build(self) -> etree.Element:
    """
    Build Social History Section XML element.

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add template IDs
    self.add_template_ids(section)

    # Add section code (29762-2 = Social History)
    code_elem = Code(
        code=self.SOCIAL_HISTORY_CODE,
        system="LOINC",
        display_name=self.SOCIAL_HISTORY_DISPLAY,
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # Add title
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (HTML table)
    self._add_narrative(section)

    # Add entries with Smoking Status Observations
    for status in self.smoking_statuses:
        self._add_entry(section, status)

    return section

VitalSignsSection

VitalSignsSection

Bases: CDAElement

Builder for C-CDA Vital Signs Section.

Includes narrative (HTML table) and structured entries. Supports both R2.1 (2015-08-01) and R2.0 (2014-06-09) versions.

Source code in ccdakit/builders/sections/vital_signs.py
class VitalSignsSection(CDAElement):
    """
    Builder for C-CDA Vital Signs Section.

    Includes narrative (HTML table) and structured entries.
    Supports both R2.1 (2015-08-01) and R2.0 (2014-06-09) versions.
    """

    # Template IDs for different versions
    TEMPLATES = {
        CDAVersion.R2_1: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.4.1",
                extension="2015-08-01",
                description="Vital Signs Section (entries required) R2.1",
            ),
        ],
        CDAVersion.R2_0: [
            TemplateConfig(
                root="2.16.840.1.113883.10.20.22.2.4.1",
                extension="2014-06-09",
                description="Vital Signs Section (entries required) R2.0",
            ),
        ],
    }

    def __init__(
        self,
        vital_signs_organizers: Sequence[VitalSignsOrganizerProtocol],
        title: str = "Vital Signs",
        version: CDAVersion = CDAVersion.R2_1,
        **kwargs,
    ):
        """
        Initialize VitalSignsSection builder.

        Args:
            vital_signs_organizers: List of vital signs organizers
            title: Section title (default: "Vital Signs")
            version: C-CDA version (R2.1 or R2.0)
            **kwargs: Additional arguments passed to CDAElement
        """
        super().__init__(version=version, **kwargs)
        self.vital_signs_organizers = vital_signs_organizers
        self.title = title

    def build(self) -> etree.Element:
        """
        Build Vital Signs Section XML element.

        Returns:
            lxml Element for section
        """
        # Create section element
        section = etree.Element(f"{{{NS}}}section")

        # Add template IDs
        self.add_template_ids(section)

        # Add section code (8716-3 = Vital signs)
        code_elem = Code(
            code="8716-3",
            system="LOINC",
            display_name="Vital signs",
        ).to_element()
        code_elem.tag = f"{{{NS}}}code"
        section.append(code_elem)

        # Add title
        title_elem = etree.SubElement(section, f"{{{NS}}}title")
        title_elem.text = self.title

        # Add narrative text (HTML table)
        self._add_narrative(section)

        # Add entries with Vital Signs Organizers
        for organizer in self.vital_signs_organizers:
            self._add_entry(section, organizer)

        return section

    def _add_narrative(self, section: etree._Element) -> None:
        """
        Add narrative text element with HTML table.

        Args:
            section: section element
        """
        # Create text element
        text = etree.SubElement(section, f"{{{NS}}}text")

        if not self.vital_signs_organizers:
            # No vital signs - add "No vital signs recorded" paragraph
            paragraph = etree.SubElement(text, f"{{{NS}}}paragraph")
            paragraph.text = "No vital signs recorded"
            return

        # Create table for vital signs
        table = etree.SubElement(text, f"{{{NS}}}table", border="1", width="100%")

        # Table header
        thead = etree.SubElement(table, f"{{{NS}}}thead")
        tr = etree.SubElement(thead, f"{{{NS}}}tr")

        headers = [
            "Date/Time",
            "Vital Sign",
            "Value",
            "Unit",
            "Interpretation",
        ]
        for header_text in headers:
            th = etree.SubElement(tr, f"{{{NS}}}th")
            th.text = header_text

        # Table body
        tbody = etree.SubElement(table, f"{{{NS}}}tbody")

        for organizer_idx, organizer in enumerate(self.vital_signs_organizers, start=1):
            for sign_idx, vital_sign in enumerate(organizer.vital_signs, start=1):
                tr = etree.SubElement(tbody, f"{{{NS}}}tr")

                # Date/Time
                td_date = etree.SubElement(tr, f"{{{NS}}}td")
                td_date.text = organizer.date.strftime("%Y-%m-%d %H:%M")

                # Vital Sign type (with ID reference)
                td_type = etree.SubElement(tr, f"{{{NS}}}td")
                content = etree.SubElement(
                    td_type,
                    f"{{{NS}}}content",
                    ID=f"vitalsign-{organizer_idx}-{sign_idx}",
                )
                content.text = vital_sign.type

                # Value
                td_value = etree.SubElement(tr, f"{{{NS}}}td")
                td_value.text = vital_sign.value

                # Unit
                td_unit = etree.SubElement(tr, f"{{{NS}}}td")
                td_unit.text = vital_sign.unit

                # Interpretation
                td_interpretation = etree.SubElement(tr, f"{{{NS}}}td")
                if vital_sign.interpretation:
                    td_interpretation.text = vital_sign.interpretation
                else:
                    td_interpretation.text = "-"

    def _add_entry(self, section: etree._Element, organizer: VitalSignsOrganizerProtocol) -> None:
        """
        Add entry element with Vital Signs Organizer.

        Args:
            section: section element
            organizer: Vital signs organizer data
        """
        # Create entry element
        entry = etree.SubElement(section, f"{{{NS}}}entry", typeCode="DRIV")

        # Create and add Vital Signs Organizer
        organizer_builder = VitalSignsOrganizer(organizer, version=self.version)
        entry.append(organizer_builder.to_element())

Functions

__init__(vital_signs_organizers, title='Vital Signs', version=CDAVersion.R2_1, **kwargs)

Initialize VitalSignsSection builder.

Parameters:

Name Type Description Default
vital_signs_organizers Sequence[VitalSignsOrganizerProtocol]

List of vital signs organizers

required
title str

Section title (default: "Vital Signs")

'Vital Signs'
version CDAVersion

C-CDA version (R2.1 or R2.0)

R2_1
**kwargs

Additional arguments passed to CDAElement

{}
Source code in ccdakit/builders/sections/vital_signs.py
def __init__(
    self,
    vital_signs_organizers: Sequence[VitalSignsOrganizerProtocol],
    title: str = "Vital Signs",
    version: CDAVersion = CDAVersion.R2_1,
    **kwargs,
):
    """
    Initialize VitalSignsSection builder.

    Args:
        vital_signs_organizers: List of vital signs organizers
        title: Section title (default: "Vital Signs")
        version: C-CDA version (R2.1 or R2.0)
        **kwargs: Additional arguments passed to CDAElement
    """
    super().__init__(version=version, **kwargs)
    self.vital_signs_organizers = vital_signs_organizers
    self.title = title

build()

Build Vital Signs Section XML element.

Returns:

Type Description
Element

lxml Element for section

Source code in ccdakit/builders/sections/vital_signs.py
def build(self) -> etree.Element:
    """
    Build Vital Signs Section XML element.

    Returns:
        lxml Element for section
    """
    # Create section element
    section = etree.Element(f"{{{NS}}}section")

    # Add template IDs
    self.add_template_ids(section)

    # Add section code (8716-3 = Vital signs)
    code_elem = Code(
        code="8716-3",
        system="LOINC",
        display_name="Vital signs",
    ).to_element()
    code_elem.tag = f"{{{NS}}}code"
    section.append(code_elem)

    # Add title
    title_elem = etree.SubElement(section, f"{{{NS}}}title")
    title_elem.text = self.title

    # Add narrative text (HTML table)
    self._add_narrative(section)

    # Add entries with Vital Signs Organizers
    for organizer in self.vital_signs_organizers:
        self._add_entry(section, organizer)

    return section