aimedicalcoding 0.3.5__tar.gz → 0.3.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {aimedicalcoding-0.3.5/aimedicalcoding.egg-info → aimedicalcoding-0.3.6}/PKG-INFO +1 -1
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/adapters/ccda/codes.py +12 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/serialize.py +7 -1
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/systems/loinc/axes.py +42 -1
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6/aimedicalcoding.egg-info}/PKG-INFO +1 -1
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/docs/GAP_SCHEMA.md +4 -2
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/setup.py +1 -1
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/LICENSE +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/MANIFEST.in +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/README.md +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/README_PYPI.md +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/__init__.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/__main__.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/adapters/__init__.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/adapters/base.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/adapters/ccda/__init__.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/adapters/ccda/adapter.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/adapters/ccda/narrative.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/adapters/ccda/sections.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/adapters/custom_json/__init__.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/adapters/custom_json/adapter.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/adapters/fhir/__init__.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/adapters/fhir/adapter.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/adapters/fhir/codes.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/adapters/fhir/mapping.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/api.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/core/__init__.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/core/checkdigits.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/core/models.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/core/system_registry.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/pipeline/__init__.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/pipeline/orchestrator.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/recovery/__init__.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/recovery/engine.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/systems/__init__.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/systems/base.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/systems/cvx/__init__.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/systems/cvx/handler.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/systems/cvx/normalize.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/systems/cvx/patterns.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/systems/icd10cm/__init__.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/systems/icd10cm/handler.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/systems/icd10cm/icdformat.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/systems/icd10cm/patterns.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/systems/loinc/__init__.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/systems/loinc/handler.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/systems/loinc/normalize.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/systems/loinc/patterns.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/systems/rxnorm/__init__.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/systems/rxnorm/handler.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/systems/rxnorm/normalize.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/systems/rxnorm/patterns.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/systems/snomed/__init__.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/systems/snomed/handler.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/systems/snomed/hierarchy.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/systems/snomed/patterns.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/systems/snomed/qualifiers.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/terminology/__init__.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/terminology/service.py +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding.egg-info/SOURCES.txt +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding.egg-info/dependency_links.txt +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding.egg-info/entry_points.txt +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding.egg-info/requires.txt +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding.egg-info/top_level.txt +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/docs/ARCHITECTURE.md +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/docs/OUTPUT_GUIDE.md +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/docs/patterns/CVX_pattern_analysis.md +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/docs/patterns/ICD10CM_pattern_analysis.md +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/docs/patterns/LOINC_pattern_analysis.md +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/docs/patterns/RxNorm_pattern_analysis.md +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/docs/patterns/SNOMED_pattern_analysis.md +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/pyproject.toml +0 -0
- {aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aimedicalcoding
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.6
|
|
4
4
|
Summary: Detect missing/broken clinical codes (LOINC / SNOMED CT / RxNorm / ICD-10-CM / CVX) in CCDA and FHIR documents, with recovery evidence
|
|
5
5
|
Author: Aakash Kag, Vinay Shankar Miryala, Siva Karthik, Mohammed Rayaan
|
|
6
6
|
License: MIT
|
|
@@ -186,6 +186,18 @@ def extract_companion(obs_el: etree._Element) -> dict:
|
|
|
186
186
|
unit = (low.get("unit") if low is not None else None) or (
|
|
187
187
|
high.get("unit") if high is not None else None)
|
|
188
188
|
out["reference_range"] = f"{lo}-{hi}" + (f" {unit}" if unit else "")
|
|
189
|
+
|
|
190
|
+
# method: the LOINC Method axis, carried structurally as <methodCode>.
|
|
191
|
+
# Prefer the displayName (e.g. "Automated") over the raw code.
|
|
192
|
+
method = obs_el.find(q("methodCode"))
|
|
193
|
+
if method is not None and (method.get("displayName") or method.get("code")):
|
|
194
|
+
out["method"] = method.get("displayName") or method.get("code")
|
|
195
|
+
|
|
196
|
+
# body site: <targetSiteCode> (usually SNOMED, e.g. "Right arm").
|
|
197
|
+
site = obs_el.find(q("targetSiteCode"))
|
|
198
|
+
if site is not None and (site.get("displayName") or site.get("code")):
|
|
199
|
+
out["body_site"] = site.get("displayName") or site.get("code")
|
|
200
|
+
out["body_site_code"] = site.get("code")
|
|
189
201
|
return out
|
|
190
202
|
|
|
191
203
|
|
|
@@ -87,14 +87,20 @@ def _qualifiers(stmt: ClinicalStatement) -> dict:
|
|
|
87
87
|
unit = stmt.value.unit if stmt.value else None
|
|
88
88
|
q = snomed_q.extract_qualifiers(stmt, stmt.raw_text)
|
|
89
89
|
out = {
|
|
90
|
-
"specimen": {"value": spec_hint, "source": spec_src, "confidence": spec_conf
|
|
90
|
+
"specimen": {"value": spec_hint, "source": spec_src, "confidence": spec_conf,
|
|
91
|
+
# explicit specimen disagrees with the analyte text (data-quality signal)
|
|
92
|
+
"conflict": loinc_axes.specimen_conflict(stmt)},
|
|
91
93
|
"property": loinc_axes.property_from_unit(unit),
|
|
92
94
|
"scale": loinc_axes.scale_from_value(stmt),
|
|
95
|
+
"method": stmt.companion.get("method"),
|
|
96
|
+
"body_site": stmt.companion.get("body_site"),
|
|
93
97
|
"laterality": q["laterality"],
|
|
94
98
|
"severity": q["severity"],
|
|
95
99
|
"course": q["course"],
|
|
96
100
|
# structural negationInd wins over text cues ("denies", "no known")
|
|
97
101
|
"negation": ("negated" if stmt.companion.get("negation") else q["negation_context"]),
|
|
102
|
+
"interpretation": stmt.companion.get("interpretation"),
|
|
103
|
+
"reference_range": stmt.companion.get("reference_range"),
|
|
98
104
|
"panel": (stmt.panel.code if stmt.panel else None),
|
|
99
105
|
"effective_time": stmt.effective_time,
|
|
100
106
|
}
|
|
@@ -14,11 +14,26 @@ from .normalize import normalize_analyte
|
|
|
14
14
|
|
|
15
15
|
# UCUM unit -> LOINC Property hint
|
|
16
16
|
_UNIT_PROPERTY = {
|
|
17
|
+
# mass concentration
|
|
17
18
|
"mg/dl": "MCnc", "g/dl": "MCnc", "ug/dl": "MCnc", "ng/ml": "MCnc",
|
|
18
|
-
"
|
|
19
|
+
"mg/l": "MCnc", "ug/l": "MCnc", "ng/dl": "MCnc", "pg/ml": "MCnc",
|
|
20
|
+
"g/l": "MCnc",
|
|
21
|
+
# substance concentration
|
|
22
|
+
"mmol/l": "SCnc", "meq/l": "SCnc", "umol/l": "SCnc", "nmol/l": "SCnc",
|
|
23
|
+
"pmol/l": "SCnc", "mol/l": "SCnc",
|
|
24
|
+
# catalytic (enzyme) concentration
|
|
25
|
+
"u/l": "CCnc", "iu/l": "CCnc", "u/ml": "CCnc",
|
|
26
|
+
# arbitrary / international-unit concentration
|
|
27
|
+
"iu/ml": "ACnc", "miu/ml": "ACnc", "iu/dl": "ACnc",
|
|
28
|
+
# number concentration (counts per volume) — CBC analytes (platelets, WBC, RBC)
|
|
29
|
+
"10*3/ul": "NCnc", "10*6/ul": "NCnc", "10*9/l": "NCnc", "10*12/l": "NCnc",
|
|
30
|
+
"10*3/l": "NCnc", "/ul": "NCnc", "/l": "NCnc", "k/ul": "NCnc",
|
|
31
|
+
"m/ul": "NCnc", "cells/ul": "NCnc", "/mm3": "NCnc",
|
|
32
|
+
# fractions / other
|
|
19
33
|
"%": "MFr", "fl": "EntVol", "pg": "EntMass",
|
|
20
34
|
"mm[hg]": "Pres", "/min": "NRat", "cel": "Temp", "kg": "Mass",
|
|
21
35
|
"cm": "Len", "kg/m2": "Ratio", "[degf]": "Temp", "/min)": "NRat",
|
|
36
|
+
"s": "Time", "mm/h": "Vel",
|
|
22
37
|
}
|
|
23
38
|
|
|
24
39
|
_SPECIMEN_HINTS = {
|
|
@@ -143,6 +158,11 @@ def infer_specimen(stmt: ClinicalStatement) -> tuple[Optional[str], str, str]:
|
|
|
143
158
|
|
|
144
159
|
|
|
145
160
|
def method_from_text(stmt: ClinicalStatement) -> Optional[str]:
|
|
161
|
+
# Structural <methodCode> (LOINC Method axis) is more reliable than text
|
|
162
|
+
# cues — use it first (e.g. "Automated"), then fall back to text keywords.
|
|
163
|
+
method = stmt.companion.get("method")
|
|
164
|
+
if method:
|
|
165
|
+
return method
|
|
146
166
|
text = (stmt.raw_text or "").lower()
|
|
147
167
|
for kw in ("fasting", "post", "challenge", "poc", "point of care", "random"):
|
|
148
168
|
if kw in text:
|
|
@@ -150,6 +170,24 @@ def method_from_text(stmt: ClinicalStatement) -> Optional[str]:
|
|
|
150
170
|
return None
|
|
151
171
|
|
|
152
172
|
|
|
173
|
+
# specimen words that name a specimen in analyte text (reuse the hint map)
|
|
174
|
+
def specimen_conflict(stmt: ClinicalStatement) -> bool:
|
|
175
|
+
"""True when an EXPLICIT specimen disagrees with a specimen named in the
|
|
176
|
+
analyte text — a data-quality signal (LOINC_pattern_analysis §1.2). E.g. an
|
|
177
|
+
explicit "Urine specimen" on a "Platelets ... in Blood" observation."""
|
|
178
|
+
explicit = None
|
|
179
|
+
if stmt.companion.get("specimen") or stmt.companion.get("specimen_code"):
|
|
180
|
+
explicit = _specimen_to_hint(stmt.companion.get("specimen"),
|
|
181
|
+
stmt.companion.get("specimen_code"))
|
|
182
|
+
if not explicit:
|
|
183
|
+
return False
|
|
184
|
+
text = (stmt.raw_text or "").lower()
|
|
185
|
+
for word, hint in _SPECIMEN_HINTS.items():
|
|
186
|
+
if word in text and hint != explicit:
|
|
187
|
+
return True
|
|
188
|
+
return False
|
|
189
|
+
|
|
190
|
+
|
|
153
191
|
def build_match_key(stmt: ClinicalStatement, expand_abbrev: bool = False) -> str:
|
|
154
192
|
"""Composite disambiguation key (component + specimen + property + scale + method).
|
|
155
193
|
|
|
@@ -191,9 +229,12 @@ def evidence_packet(stmt: ClinicalStatement, expand_abbrev: bool = False) -> dic
|
|
|
191
229
|
"specimen_hint": specimen_hint,
|
|
192
230
|
"specimen_source": specimen_source, # explicit | text | panel | none
|
|
193
231
|
"specimen_confidence": specimen_conf, # high | medium | low | none
|
|
232
|
+
"specimen_conflict": specimen_conflict(stmt), # explicit vs analyte text
|
|
194
233
|
"method_hint": method_from_text(stmt),
|
|
195
234
|
"value": stmt.value.raw if stmt.value else None,
|
|
196
235
|
"reference_range": stmt.companion.get("reference_range"),
|
|
236
|
+
"interpretation": stmt.companion.get("interpretation"),
|
|
237
|
+
"body_site": stmt.companion.get("body_site"),
|
|
197
238
|
"panel": (stmt.panel.code if stmt.panel else None),
|
|
198
239
|
"context": stmt.context,
|
|
199
240
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aimedicalcoding
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.6
|
|
4
4
|
Summary: Detect missing/broken clinical codes (LOINC / SNOMED CT / RxNorm / ICD-10-CM / CVX) in CCDA and FHIR documents, with recovery evidence
|
|
5
5
|
Author: Aakash Kag, Vinay Shankar Miryala, Siva Karthik, Mohammed Rayaan
|
|
6
6
|
License: MIT
|
|
@@ -63,9 +63,11 @@ Each record separates four concerns:
|
|
|
63
63
|
"codings": [ ... ]
|
|
64
64
|
},
|
|
65
65
|
"qualifiers": { // disambiguation context (superset; nulls where N/A)
|
|
66
|
-
"specimen": { "value","source","confidence" },
|
|
67
|
-
"property": "MCnc", "scale": "Qn", // LOINC
|
|
66
|
+
"specimen": { "value","source","confidence","conflict" }, // LOINC; conflict=explicit specimen disagrees with analyte text
|
|
67
|
+
"property": "MCnc", "scale": "Qn", // LOINC (property from UCUM unit; NCnc for counts, etc.)
|
|
68
|
+
"method": "Automated", "body_site": "Right arm", // LOINC Method axis (<methodCode>) / <targetSiteCode>
|
|
68
69
|
"laterality": null, "severity": null, "course": null, "negation": null, // SNOMED
|
|
70
|
+
"interpretation": "N", "reference_range": "150-400 10*3/uL", // observation companion data
|
|
69
71
|
"panel": "24323-8", "effective_time": null
|
|
70
72
|
}
|
|
71
73
|
}
|
|
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|
|
2
2
|
|
|
3
3
|
setup(
|
|
4
4
|
name="aimedicalcoding",
|
|
5
|
-
version="0.3.
|
|
5
|
+
version="0.3.6",
|
|
6
6
|
author="Aakash Kag, Vinay Shankar Miryala, Siva Karthik, Mohammed Rayaan",
|
|
7
7
|
description=(
|
|
8
8
|
"Detect missing/broken clinical codes (LOINC / SNOMED CT / RxNorm / "
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/adapters/custom_json/__init__.py
RENAMED
|
File without changes
|
{aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/adapters/custom_json/adapter.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/systems/icd10cm/icdformat.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding/systems/snomed/qualifiers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aimedicalcoding-0.3.5 → aimedicalcoding-0.3.6}/aimedicalcoding.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|