Observed since 21 Oct, 11:17 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
Detect missing/broken clinical codes (LOINC / SNOMED CT / RxNorm / ICD-10-CM / CVX) in CCDA and FHIR documents, with recovery evidence
Find every missing or broken standard clinical code in a CCDA or FHIR document — with the evidence needed to fix it.
aimedicalcoding parses a clinical document (CCDA XML or FHIR R4 Bundle JSON),
inspects every coded clinical item — labs, vitals, problems, medications,
immunizations, allergies, procedures, social history — and reports the ones
whose standard terminology code (LOINC · SNOMED CT · RxNorm · ICD-10-CM ·
CVX) is missing, local-only, crosswalkable, or malformed. Each finding (a
gap) is emitted as a self-contained JSON record carrying the classification,
the document facts, and a ready-made recovery plan (search terms, axis filters,
crosswalk candidates, check-digit corrections).
Items that are already correctly coded produce no output — the result is a work list, not an inventory.
pip install aimedicalcoding
Requires Python 3.10+. The only runtime dependency is lxml.
The input format is auto-detected — the same command works for CCDA XML and FHIR JSON, and the output schema is identical for both.
# human-readable gap report (card view)
aimedicalcoding patient.xml
# FHIR R4 Bundle — same flags, same output
aimedicalcoding bundle.json --table
# counts only
aimedicalcoding patient.xml --summary --table
# machine-readable JSON, written to a file as UTF-8
aimedicalcoding patient.xml --out gaps.json
# restrict to one terminology and/or certain contexts
aimedicalcoding patient.xml --system loinc --contexts result,vital
If the entry point is not on your PATH, python -m aimedicalcoding <file> [flags]
is equivalent.
| Flag | Meaning |
|---|---|
--system | loinc | snomed | rxnorm | icd10cm | cvx | all (default all) |
--contexts | comma-separated contexts to include, e.g. result,vital,problem |
--table | compact one-line-per-gap table |
--json | print the JSON report to stdout |
-o, --out FILE | write the JSON report to FILE as UTF-8 (implies --json) |
--summary | counts only |
--include-narrative | also report narrative-table-only items (CCDA; off by default) |
--apply-dict | expand lab abbreviations (BUN → urea nitrogen) in search terms |
--json-full | dump the complete internal representation (debugging) |
from aimedicalcoding import extract_gaps
result = extract_gaps("patient.xml") # CCDA — auto-detected
result = extract_gaps("bundle.json") # FHIR R4 Bundle — auto-detected
result = extract_gaps(raw_bytes, # bytes / str / path / file-like
systems=("loinc", "snomed", "rxnorm", "icd10cm", "cvx"))
len(result) # number of gaps
result.by_target # {"LOINC": 17, "SNOMED-CT": 30, "RxNorm": 4, ...}
result.by_pattern # {"LOCAL_CODE_ONLY": 15, "NULLFLAVOR": 32, ...}
result.gaps # list[dict] — one record per (item, missing terminology)
result.to_json() # the same JSON the CLI emits
Lower-level access — parse to the intermediate representation without gap analysis:
from aimedicalcoding.pipeline import parse_document
statements = parse_document(open("bundle.json", "rb").read())
for s in statements:
print(s.context, s.raw_text, [(c.system, c.code) for c in s.codings])
The library is built on two independent axes that meet at a format-agnostic intermediate representation (IR):
FORMAT axis (adapters) TERMINOLOGY axis (extractors)
CCDA XML ──┐ LOINC SNOMED CT
FHIR R4 ───┤──► ClinicalStatement ──► RxNorm ICD-10-CM CVX
│ (the IR)
(auto-detected) (identical for every input format)
<ClinicalDocument> root → CCDA adapter; JSON with
"resourceType": "Bundle" → FHIR adapter.ClinicalStatement per clinical item: every coding found on the item (root
code plus all translations / coding[] entries, each canonicalized by
system), the human labels, the value (quantity or coded answer), specimen,
panel membership, timestamps.CCDA: document/section/entry structure, <code> + <translation>
scan-and-identify, nullFlavor, <originalText> reference resolution into the
narrative, organizer → panel linkage, specimen, interpretation and reference
ranges, narrative-table fallback for sections without structured entries.
FHIR R4 Bundle: routing by resource type + Observation.category (with
section-tag fallback), CodeableConcept.coding[] as primary + translations,
valueQuantity / valueCodeableConcept / valueString / dataAbsentReason,
hasMember / DiagnosticReport.result panel linkage (children inherit context
and specimen), medicationReference → Medication.code and
specimen → Specimen.type resolution across the Bundle, per-reaction allergy
statements, and normalization of "unknown" null-marker strings.
Each record's pattern says why the code is missing — which determines how
to fix it:
| Pattern | What's wrong | Recovery route |
|---|---|---|
LOCAL_CODE_ONLY | a local/in-house/CPT code, no standard code | crosswalk local → target, else text search |
NULLFLAVOR | code explicitly declared unknown (nullFlavor, "unknown", dataAbsentReason) | resolve the recovered label |
DISPLAY_ONLY | only a display name, no code and no absence marker | resolve the label |
NARRATIVE_ONLY | exists only as narrative table text (CCDA, opt-in) | text search, low priority |
BAD_CODE | right shape, fails validation (LOINC Luhn · SNOMED Verhoeff · ICD format) | check-digit / format correction (suggested fix included) |
ICD_ONLY | ICD present, SNOMED missing | official ICD ↔ SNOMED map |
SNOMED_ONLY | SNOMED present, target missing (ICD-10-CM; or LOINC on a lab/functional item) | SNOMED → ICD-10-CM / SNOMED → LOINC map |
CPT_ONLY | CPT present, no LOINC | CPT → LOINC crosswalk |
NDC_ONLY | NDC present, RxNorm/CVX missing | NDC ↔ RxNorm / CDC NDC ↔ CVX table |
RXNORM_ONLY | RxNorm present, target missing (vaccine → CVX; drug allergen → SNOMED) | CVX ↔ RxNorm map / RxNorm ↔ SNOMED via UMLS |
MULTUM_ONLY / FDB_ONLY / MEDISPAN_ONLY | proprietary drug vocabulary, RxNorm missing | vendor ↔ RxNorm via UMLS |
The JSON envelope:
{
"source": "patient.xml",
"source_format": "ccda", // or "fhir" — auto-detected
"targets_checked": ["LOINC", "SNOMED-CT", "RxNorm", "ICD-10-CM", "CVX"],
"gap_count": 4,
"by_pattern": { "LOCAL_CODE_ONLY": 1, "NULLFLAVOR": 3 },
"by_target": { "LOINC": 1, "SNOMED-CT": 3 },
"gaps": [ ... ]
}
Each gap record separates four concerns — the classification, the facts, the plan, and the (pending) answer:
{
"id": 1,
"target": { "system": "LOINC", "slot": "code" }, // what's missing, and in which slot
"pattern": "LOCAL_CODE_ONLY", // why
"section": { "code": "30954-2", "name": "Results", "system": "LOINC" },
"observation": { // the facts, verbatim
"code": {
"display_text": "LIPID PANEL",
"original_text": "LIPID PANEL",
"codings": [
{ "code": null, "system": "LOINC", "system_raw": "http://loinc.org" },
{ "code": "80061", "system": "CPT", "system_raw": "http://www.ama-assn.org/go/cpt" }
]
},
"value": null,
"qualifiers": { "specimen": { "value": "Bld", "confidence": "low" }, "...": "..." }
},
"recovery": { // the plan
"search_terms": ["LIPID PANEL", "lipid panel"],
"match_key": "component:LIPID PANEL | specimen:Bld | property:? | scale:? | method:-",
"crosswalk": { "from_system": "http://www.ama-assn.org/go/cpt", "from_code": "80061" },
"suggested_action": "Try (source_system, local_code) crosswalk FIRST; ..."
},
"provenance": { "location": { "type": "fhirpath", "path": "..." } },
"resolution": { "status": "pending", "code": null, "...": "..." } // filled by your recovery step
}
Reading it: this lab was coded with CPT 80061 but its LOINC slot is empty
(code: null — the source wrote a null marker). The record hands you the CPT
crosswalk candidate, the text to search with, and the LOINC axis hints
(specimen/property/scale) to constrain candidates — everything a
crosswalk-database or LLM step needs to fill resolution.
resolution is intentionally left pending
for your downstream step.urn:oid: forms and
free-text names for 20+ vocabularies (Epic/local OIDs are recognized as
local codes, HL7 structural vocabularies are excluded from gap detection).MIT — © 2026 Trove Health.
Download counts and popularity will appear here as the collector receives them.
| Version | Published | Direct dependencies | Open version |
|---|---|---|---|
| 0.3.17 | 17 Jul 2026 | 4 |
This describes our collected data; it is not a security assessment.
Observed since 21 Oct, 11:17 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:f0d1afdded2f487422c837c65563c292e9013d440d118977b1e9ed961aff5910