annotating-variants
Annotates VCF variants and normalizes HGVS nomenclature with public, license-free annotators…
Parses C-CDA / CCD XML clinical documents to extract human-readable section narrative plus coded entries, keyed by section LOINC codes and templateIds. Use before OpenMed processing when ingesting C-CDA R2.1 documents (CCD, Discharge Summary, H&P, Consultation Note) exported
$ npx -y skills add maziyarpanahi/openmed --skill parsing-ccda-documents --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/parsing-ccda-documentsContext preview
The summary Claude sees to decide when to auto-load this skill.
Parses C-CDA / CCD XML clinical documents to extract human-readable section narrative plus coded entries, keyed by section LOINC codes and templateIds. Use before OpenMed processing when ingesting C-CDA R2.1 documents (CCD, Discharge Summary, H&P, Consultation Note) exported
name: parsing-ccda-documents description: "Parses C-CDA / CCD XML clinical documents to extract human-readable section narrative plus coded entries, keyed by section LOINC codes and templateIds. Use before OpenMed processing when ingesting C-CDA R2.1 documents (CCD, Discharge Summary, H&P, Consultation Note) exported from an EHR and you need the narrative section text de-identified and analyzed. Hand section narrative to openmed.deidentify and openmed.analyze_text; XML-aware de-identification that preserves CDA markup is available via openmed.interop.cda. Trigger keywords: C-CDA, CCD, CDA, clinical document, templateId, LOINC section, narrative block, discharge summary XML, ClinicalDocument." license: Apache-2.0 metadata: project: OpenMed category: data-ingestion pairs: before version: "1.0"
C-CDA (Consolidated Clinical Document Architecture) is the XML document standard behind Meaningful Use / ONC certification — the CCD, Discharge Summary, History & Physical, and Consultation Note you get when an EHR "exports a chart". Each document is a `ClinicalDocument` with a header (patient, authors, encounter) and a `structuredBody` of **sections**. Every section has *two* representations: a human-readable **narrative `<text>` block** and machine-readable **coded entries**. The narrative is what you feed to clinical NLP. This skill extracts it and hands it to OpenMed.
export, HIE) and want the free-text section narrative for de-id and NER.
meds, allergies, results, plan, H&P narrative).
<ClinicalDocument xmlns="urn:hl7-org:v3">
<recordTarget><patientRole>
<id extension="12345" root="..."/>
<patient><name><given>Jane</given><family>Doe</family></name>
<birthTime value="19700115"/></patient>
</patientRole></recordTarget>
<component><structuredBody>
<component><section>
<templateId root="2.16.840.1.113883.10.20.22.2.5.1"/> <!-- Problems -->
<code code="11450-4" codeSystem="2.16.840.1.113883.6.1"/> <!-- LOINC -->
<title>Problems</title>
<text>Active problems: Type 2 diabetes, hypertension.</text> <!-- narrative -->
<entry>...coded SNOMED/ICD entries...</entry>
</section></component>
</structuredBody></component>
</ClinicalDocument>Sections are identified by **`templateId/@root`** and by **section `code`** (LOINC). The CDA namespace is `urn:hl7-org:v3`.
Extract section narrative by LOINC code, then hand off to OpenMed:
import openmed
from xml.etree import ElementTree as ET
NS = {"hl7": "urn:hl7-org:v3"}
SECTION_LOINC = {
"11450-4": "problems", "10160-0": "medications", "48765-2": "allergies",
"30954-2": "results", "18776-5": "plan", "10164-2": "hpi",
"8648-8": "hospital_course", "11488-4": "consult_note",
}
root = ET.parse("ccd.xml").getroot()
for section in root.findall(".//hl7:section", NS):
code_el = section.find("hl7:code", NS)
loinc = code_el.get("code") if code_el is not None else None
text_el = section.find("hl7:text", NS)
if text_el is None:
continue
narrative = "".join(text_el.itertext()).strip() # flatten narrative block
if not narrative:
continue
deid = openmed.deidentify(narrative, method="replace", policy="hipaa_safe_harbor")
result = openmed.analyze_text(deid.text, output_format="dict")
section_name = SECTION_LOINC.get(loinc, loinc)
# attach (section_name, result) for downstream consumers`"".join(text_el.itertext())` flattens the narrative block (which may contain `<paragraph>`, `<list>`, `<table>`, `<content>` markup) into plain text.
When you need to redact PHI from the *document* (header ids, names, addresses, dates) while keeping the CDA XML valid and parseable, use the bundled adapter rather than regexing the raw XML:
from openmed.interop.cda import redact_cda, is_cda_document
if is_cda_document("ccd.xml"):
safe_xml = redact_cda("ccd.xml") # returns redacted XML string`redact_cda` applies `DEFAULT_PHI_ELEMENT_MAP` (patient id hashed, name/address/ telecom null-flavored, birthTime and effectiveTime date-shifted) to header elements *and* sweeps section narrative text — operating on text nodes only so surrounding markup stays intact. Pass `text_redactor=` to plug an extra free-text callback (e.g. an `openmed.deidentify` wrapper), `date_shift_days=` for a fixed shift, and `keep_year=True` to preserve years.
1. **Confirm it's CDA.** `is_cda_document(...)` checks for a `ClinicalDocument` root. Reject XML with `DOCTYPE`/`ENTITY` declarations (XXE risk) — the adapter does this for you. 2. **Read the header** for context: patient, author, `effectiveTime`, `documentType` (`ClinicalDocument/code` LOINC). Treat all header values as PHI. 3. **Walk sections** by `templateId` or section `code` (LOINC). Map to your section vocabulary. 4. **Flatten narrative** `<text>` with `itertext()`; preserve the section→text association for span attribution. 5. **De-identify → analyze** each narrative with OpenMed. Prefer coded `<entry>` data when it already exists; use NLP to recover what is *only* in narrative.
`openmed.analyze_text`. Keep `(section LOINC, narrative)` so entities trace back to their section.
`PhiElementRule`, and `DEFAULT_PHI_ELEMENT_MAP` for namespace-aware, markup-preserving de-identification. It also registers an `.xml` document handler with OpenMed's multimodal int
Local-first healthcare AI: clinical NER and HIPAA PII de-identification on hardware you control. 2,200+ medical models, 35 model-backed PII languages, and Python, MLX, Android and browser runtimes. Apache-2.0 SDK.
Repo: maziyarpanahi/openmed
Annotates VCF variants and normalizes HGVS nomenclature with public, license-free annotators…
Choose the first OpenMed workflow skill for an intake, privacy, extraction, exchange, or…
Package multiple FHIR R4 resources produced from OpenMed output into a single valid…
Adversarially scan already-de-identified clinical text for residual identifiers and emit a…
Produce a signed, reproducible, no-PHI audit trail for an OpenMed de-identification run via…
Generates and verifies 21 CFR Part 11-style audit trails — who/what/when, electronic…