hccinfhir 0.1.3__py3-none-any.whl → 0.1.5__py3-none-any.whl
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.
- hccinfhir/__init__.py +2 -2
- hccinfhir/datamodels.py +17 -15
- hccinfhir/extractor_837.py +89 -22
- hccinfhir/hccinfhir.py +6 -5
- hccinfhir/model_calculate.py +19 -15
- hccinfhir/model_interactions.py +1 -0
- hccinfhir/sample_files/sample_837_12.txt +113 -0
- hccinfhir/samples.py +15 -15
- hccinfhir-0.1.5.dist-info/METADATA +693 -0
- hccinfhir-0.1.5.dist-info/RECORD +49 -0
- hccinfhir/sample_utils.py +0 -252
- hccinfhir-0.1.3.dist-info/METADATA +0 -390
- hccinfhir-0.1.3.dist-info/RECORD +0 -49
- /hccinfhir/{samples → sample_files}/__init__.py +0 -0
- /hccinfhir/{samples → sample_files}/sample_837_0.txt +0 -0
- /hccinfhir/{samples → sample_files}/sample_837_1.txt +0 -0
- /hccinfhir/{samples → sample_files}/sample_837_10.txt +0 -0
- /hccinfhir/{samples → sample_files}/sample_837_11.txt +0 -0
- /hccinfhir/{samples → sample_files}/sample_837_2.txt +0 -0
- /hccinfhir/{samples → sample_files}/sample_837_3.txt +0 -0
- /hccinfhir/{samples → sample_files}/sample_837_4.txt +0 -0
- /hccinfhir/{samples → sample_files}/sample_837_5.txt +0 -0
- /hccinfhir/{samples → sample_files}/sample_837_6.txt +0 -0
- /hccinfhir/{samples → sample_files}/sample_837_7.txt +0 -0
- /hccinfhir/{samples → sample_files}/sample_837_8.txt +0 -0
- /hccinfhir/{samples → sample_files}/sample_837_9.txt +0 -0
- /hccinfhir/{samples → sample_files}/sample_eob_1.json +0 -0
- /hccinfhir/{samples → sample_files}/sample_eob_2.json +0 -0
- /hccinfhir/{samples → sample_files}/sample_eob_200.ndjson +0 -0
- /hccinfhir/{samples → sample_files}/sample_eob_3.json +0 -0
- {hccinfhir-0.1.3.dist-info → hccinfhir-0.1.5.dist-info}/WHEEL +0 -0
- {hccinfhir-0.1.3.dist-info → hccinfhir-0.1.5.dist-info}/licenses/LICENSE +0 -0
hccinfhir/__init__.py
CHANGED
|
@@ -12,7 +12,7 @@ from .model_calculate import calculate_raf
|
|
|
12
12
|
from .datamodels import Demographics, ServiceLevelData, RAFResult, ModelName
|
|
13
13
|
|
|
14
14
|
# Sample data functions
|
|
15
|
-
from .
|
|
15
|
+
from .samples import (
|
|
16
16
|
SampleData,
|
|
17
17
|
get_eob_sample,
|
|
18
18
|
get_eob_sample_list,
|
|
@@ -21,7 +21,7 @@ from .sample_utils import (
|
|
|
21
21
|
list_available_samples
|
|
22
22
|
)
|
|
23
23
|
|
|
24
|
-
__version__ = "0.1.
|
|
24
|
+
__version__ = "0.1.5"
|
|
25
25
|
__author__ = "Yubin Park"
|
|
26
26
|
__email__ = "yubin.park@mimilabs.ai"
|
|
27
27
|
|
hccinfhir/datamodels.py
CHANGED
|
@@ -89,18 +89,20 @@ class Demographics(BaseModel):
|
|
|
89
89
|
pbd: Optional[bool] = Field(False, description="[derived] True if PBD (PBD Model)")
|
|
90
90
|
|
|
91
91
|
|
|
92
|
-
class RAFResult(
|
|
93
|
-
"""
|
|
94
|
-
risk_score: float
|
|
95
|
-
risk_score_demographics: float
|
|
96
|
-
risk_score_chronic_only: float
|
|
97
|
-
risk_score_hcc: float
|
|
98
|
-
hcc_list: List[str]
|
|
99
|
-
cc_to_dx: Dict[str, Set[str]]
|
|
100
|
-
coefficients: Dict[str, float]
|
|
101
|
-
interactions: Dict[str, float]
|
|
102
|
-
demographics: Demographics
|
|
103
|
-
model_name: ModelName
|
|
104
|
-
version: str
|
|
105
|
-
diagnosis_codes: List[str]
|
|
106
|
-
service_level_data: Optional[List[ServiceLevelData]]
|
|
92
|
+
class RAFResult(BaseModel):
|
|
93
|
+
"""Risk adjustment calculation results"""
|
|
94
|
+
risk_score: float = Field(..., description="Final RAF score")
|
|
95
|
+
risk_score_demographics: float = Field(..., description="Demographics-only risk score")
|
|
96
|
+
risk_score_chronic_only: float = Field(..., description="Chronic conditions risk score")
|
|
97
|
+
risk_score_hcc: float = Field(..., description="HCC conditions risk score")
|
|
98
|
+
hcc_list: List[str] = Field(default_factory=list, description="List of active HCC categories")
|
|
99
|
+
cc_to_dx: Dict[str, Set[str]] = Field(default_factory=dict, description="Condition categories mapped to diagnosis codes")
|
|
100
|
+
coefficients: Dict[str, float] = Field(default_factory=dict, description="Applied model coefficients")
|
|
101
|
+
interactions: Dict[str, float] = Field(default_factory=dict, description="Disease interaction coefficients")
|
|
102
|
+
demographics: Demographics = Field(..., description="Patient demographics used in calculation")
|
|
103
|
+
model_name: ModelName = Field(..., description="HCC model used for calculation")
|
|
104
|
+
version: str = Field(..., description="Library version")
|
|
105
|
+
diagnosis_codes: List[str] = Field(default_factory=list, description="Input diagnosis codes")
|
|
106
|
+
service_level_data: Optional[List[ServiceLevelData]] = Field(default=None, description="Processed service records")
|
|
107
|
+
|
|
108
|
+
model_config = {"extra": "forbid", "validate_assignment": True}
|
hccinfhir/extractor_837.py
CHANGED
|
@@ -83,7 +83,58 @@ def process_service_line(segments: List[List[str]], start_index: int) -> tuple[O
|
|
|
83
83
|
|
|
84
84
|
return ndc, service_date
|
|
85
85
|
|
|
86
|
-
def
|
|
86
|
+
def split_into_claims(segments: List[List[str]]) -> List[List[List[str]]]:
|
|
87
|
+
"""Split segments into individual claims based on ST/SE boundaries.
|
|
88
|
+
|
|
89
|
+
Each ST...SE block represents one complete claim.
|
|
90
|
+
Returns a list of claim segment lists.
|
|
91
|
+
"""
|
|
92
|
+
claims = []
|
|
93
|
+
current_claim = []
|
|
94
|
+
in_transaction = False
|
|
95
|
+
st_control_number = None
|
|
96
|
+
|
|
97
|
+
for segment in segments:
|
|
98
|
+
if len(segment) < 1:
|
|
99
|
+
continue
|
|
100
|
+
|
|
101
|
+
seg_id = segment[0]
|
|
102
|
+
|
|
103
|
+
if seg_id == 'ST':
|
|
104
|
+
# Start new claim transaction
|
|
105
|
+
if current_claim: # Save previous claim if exists (shouldn't happen with valid X12)
|
|
106
|
+
claims.append(current_claim)
|
|
107
|
+
current_claim = [segment]
|
|
108
|
+
in_transaction = True
|
|
109
|
+
st_control_number = segment[2] if len(segment) > 2 else None
|
|
110
|
+
|
|
111
|
+
elif seg_id == 'SE':
|
|
112
|
+
# End current claim transaction
|
|
113
|
+
if in_transaction:
|
|
114
|
+
current_claim.append(segment)
|
|
115
|
+
|
|
116
|
+
# Validate control numbers match (ST02 == SE02)
|
|
117
|
+
se_control_number = segment[2] if len(segment) > 2 else None
|
|
118
|
+
if st_control_number != se_control_number:
|
|
119
|
+
print(f"Warning: ST/SE control numbers don't match: {st_control_number} != {se_control_number}")
|
|
120
|
+
|
|
121
|
+
claims.append(current_claim)
|
|
122
|
+
current_claim = []
|
|
123
|
+
in_transaction = False
|
|
124
|
+
st_control_number = None
|
|
125
|
+
|
|
126
|
+
elif in_transaction:
|
|
127
|
+
# Add segment to current claim
|
|
128
|
+
current_claim.append(segment)
|
|
129
|
+
|
|
130
|
+
# Handle case where file doesn't end with SE (malformed)
|
|
131
|
+
if current_claim:
|
|
132
|
+
print("Warning: Unclosed transaction found (missing SE)")
|
|
133
|
+
claims.append(current_claim)
|
|
134
|
+
|
|
135
|
+
return claims
|
|
136
|
+
|
|
137
|
+
def parse_837_claim_to_sld(segments: List[List[str]], claim_type: str) -> List[ServiceLevelData]:
|
|
87
138
|
"""Extract service level data from 837 Professional or Institutional claims
|
|
88
139
|
|
|
89
140
|
Structure:
|
|
@@ -96,27 +147,12 @@ def extract_sld_837(content: str) -> List[ServiceLevelData]:
|
|
|
96
147
|
└── Service Line N (2400)
|
|
97
148
|
|
|
98
149
|
"""
|
|
99
|
-
|
|
100
|
-
raise ValueError("Input X12 data cannot be empty")
|
|
101
|
-
|
|
102
|
-
# Split content into segments
|
|
103
|
-
segments = [seg.strip().split('*') for seg in content.split('~') if seg.strip()]
|
|
104
|
-
|
|
105
|
-
# Detect claim type from GS segment
|
|
106
|
-
claim_type = None
|
|
107
|
-
for segment in segments:
|
|
108
|
-
if segment[0] == 'GS' and len(segment) > 8:
|
|
109
|
-
claim_type = CLAIM_TYPES.get(segment[8])
|
|
110
|
-
break
|
|
111
|
-
|
|
112
|
-
if not claim_type:
|
|
113
|
-
raise ValueError("Invalid or unsupported 837 format")
|
|
114
|
-
|
|
115
|
-
encounters = []
|
|
150
|
+
slds = []
|
|
116
151
|
current_data = ClaimData(claim_type=claim_type)
|
|
117
152
|
in_claim_loop = False
|
|
118
153
|
in_rendering_provider_loop = False
|
|
119
|
-
|
|
154
|
+
claim_control_number = None
|
|
155
|
+
|
|
120
156
|
for i, segment in enumerate(segments):
|
|
121
157
|
if len(segment) < 2:
|
|
122
158
|
continue
|
|
@@ -124,7 +160,10 @@ def extract_sld_837(content: str) -> List[ServiceLevelData]:
|
|
|
124
160
|
seg_id = segment[0]
|
|
125
161
|
|
|
126
162
|
# Process NM1 segments (Provider and Patient info)
|
|
127
|
-
if seg_id == '
|
|
163
|
+
if seg_id == 'ST':
|
|
164
|
+
claim_control_number = segment[2] if len(segment) > 2 else None
|
|
165
|
+
|
|
166
|
+
elif seg_id == 'NM1' and len(segment) > 1:
|
|
128
167
|
if segment[1] == 'IL': # Subscriber/Patient
|
|
129
168
|
current_data.patient_id = get_segment_value(segment, 9)
|
|
130
169
|
in_claim_loop = False
|
|
@@ -236,6 +275,34 @@ def extract_sld_837(content: str) -> List[ServiceLevelData]:
|
|
|
236
275
|
ndc=ndc,
|
|
237
276
|
allowed_amount=None
|
|
238
277
|
)
|
|
239
|
-
|
|
278
|
+
slds.append(service_data)
|
|
279
|
+
|
|
280
|
+
return slds
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def extract_sld_837(content: str) -> List[ServiceLevelData]:
|
|
284
|
+
|
|
285
|
+
if not content:
|
|
286
|
+
raise ValueError("Input X12 data cannot be empty")
|
|
287
|
+
|
|
288
|
+
# Split content into segments
|
|
289
|
+
segments = [seg.strip().split('*')
|
|
290
|
+
for seg in content.split('~') if seg.strip()]
|
|
291
|
+
|
|
292
|
+
# Detect claim type from GS segment
|
|
293
|
+
claim_type = None
|
|
294
|
+
for segment in segments:
|
|
295
|
+
if segment[0] == 'GS' and len(segment) > 8:
|
|
296
|
+
claim_type = CLAIM_TYPES.get(segment[8])
|
|
297
|
+
break
|
|
298
|
+
|
|
299
|
+
if not claim_type:
|
|
300
|
+
raise ValueError("Invalid or unsupported 837 format")
|
|
301
|
+
|
|
302
|
+
split_segments = split_into_claims(segments)
|
|
303
|
+
slds = []
|
|
304
|
+
for claim_segments in split_segments:
|
|
305
|
+
slds.extend(parse_837_claim_to_sld(claim_segments, claim_type))
|
|
306
|
+
|
|
307
|
+
return slds
|
|
240
308
|
|
|
241
|
-
return encounters
|
hccinfhir/hccinfhir.py
CHANGED
|
@@ -88,8 +88,9 @@ class HCCInFHIR:
|
|
|
88
88
|
# Calculate RAF score
|
|
89
89
|
unique_dx_codes = self._get_unique_diagnosis_codes(sld_list)
|
|
90
90
|
raf_result = self._calculate_raf_from_demographics(unique_dx_codes, demographics)
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
|
|
92
|
+
# Create new result with service data included
|
|
93
|
+
return raf_result.model_copy(update={'service_level_data': sld_list})
|
|
93
94
|
|
|
94
95
|
def run_from_service_data(self, service_data: List[Union[ServiceLevelData, Dict[str, Any]]],
|
|
95
96
|
demographics: Union[Demographics, Dict[str, Any]]) -> RAFResult:
|
|
@@ -122,9 +123,9 @@ class HCCInFHIR:
|
|
|
122
123
|
# Calculate RAF score
|
|
123
124
|
unique_dx_codes = self._get_unique_diagnosis_codes(standardized_data)
|
|
124
125
|
raf_result = self._calculate_raf_from_demographics(unique_dx_codes, demographics)
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
return raf_result
|
|
126
|
+
|
|
127
|
+
# Create new result with service data included
|
|
128
|
+
return raf_result.model_copy(update={'service_level_data': standardized_data})
|
|
128
129
|
|
|
129
130
|
def calculate_from_diagnosis(self, diagnosis_codes: List[str],
|
|
130
131
|
demographics: Union[Demographics, Dict[str, Any]]) -> RAFResult:
|
hccinfhir/model_calculate.py
CHANGED
|
@@ -83,9 +83,11 @@ def calculate_raf(diagnosis_codes: List[str],
|
|
|
83
83
|
coefficients = apply_coefficients(demographics, hcc_set, interactions, model_name)
|
|
84
84
|
|
|
85
85
|
hcc_chronic = set()
|
|
86
|
+
interactions_chronic = {}
|
|
86
87
|
for hcc in hcc_set:
|
|
87
88
|
if is_chronic_mapping.get((hcc, model_name), False):
|
|
88
89
|
hcc_chronic.add(hcc)
|
|
90
|
+
interactions_chronic = apply_interactions(demographics, hcc_chronic, model_name)
|
|
89
91
|
|
|
90
92
|
demographic_interactions = {}
|
|
91
93
|
for key, value in interactions.items():
|
|
@@ -104,29 +106,31 @@ def calculate_raf(diagnosis_codes: List[str],
|
|
|
104
106
|
model_name)
|
|
105
107
|
coefficients_chronic_only = apply_coefficients(demographics,
|
|
106
108
|
hcc_chronic,
|
|
107
|
-
|
|
109
|
+
interactions_chronic,
|
|
108
110
|
model_name)
|
|
109
111
|
|
|
110
112
|
# Calculate risk scores
|
|
113
|
+
print(f"Coefficients: {coefficients}")
|
|
111
114
|
risk_score = sum(coefficients.values())
|
|
115
|
+
print(f"Risk Score: {risk_score}")
|
|
112
116
|
risk_score_demographics = sum(coefficients_demographics.values())
|
|
113
117
|
risk_score_chronic_only = sum(coefficients_chronic_only.values()) - risk_score_demographics
|
|
114
118
|
risk_score_hcc = risk_score - risk_score_demographics
|
|
115
119
|
|
|
116
|
-
return
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
120
|
+
return RAFResult(
|
|
121
|
+
risk_score=risk_score,
|
|
122
|
+
risk_score_demographics=risk_score_demographics,
|
|
123
|
+
risk_score_chronic_only=risk_score_chronic_only,
|
|
124
|
+
risk_score_hcc=risk_score_hcc,
|
|
125
|
+
hcc_list=list(hcc_set),
|
|
126
|
+
cc_to_dx=cc_to_dx,
|
|
127
|
+
coefficients=coefficients,
|
|
128
|
+
interactions=interactions,
|
|
129
|
+
demographics=demographics,
|
|
130
|
+
model_name=model_name,
|
|
131
|
+
version=version,
|
|
132
|
+
diagnosis_codes=diagnosis_codes,
|
|
133
|
+
)
|
|
130
134
|
|
|
131
135
|
|
|
132
136
|
|
hccinfhir/model_interactions.py
CHANGED
|
@@ -176,6 +176,7 @@ def create_disease_interactions(model_name: ModelName,
|
|
|
176
176
|
'HF_CHR_LUNG_V28': diagnostic_cats['HF_V28'] * diagnostic_cats['CHR_LUNG_V28'],
|
|
177
177
|
'HF_KIDNEY_V28': diagnostic_cats['HF_V28'] * diagnostic_cats['KIDNEY_V28'],
|
|
178
178
|
'CHR_LUNG_CARD_RESP_FAIL_V28': diagnostic_cats['CHR_LUNG_V28'] * diagnostic_cats['CARD_RESP_FAIL_V28'],
|
|
179
|
+
'HF_HCC238_V28': diagnostic_cats['HF_V28'] * int('238' in hcc_set),
|
|
179
180
|
'gSubUseDisorder_gPsych_V28': diagnostic_cats['gSubUseDisorder_V28'] * diagnostic_cats['gPsychiatric_V28'],
|
|
180
181
|
'DISABLED_CANCER_V28': demographics.disabled * diagnostic_cats['CANCER_V28'],
|
|
181
182
|
'DISABLED_NEURO_V28': demographics.disabled * diagnostic_cats['NEURO_V28'],
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
ISA*00* *00* *ZZ*SUBMITTER ID *ZZ*RECEIVER ID *230516*1145*^*00501*000000001*0*P*:~
|
|
2
|
+
GS*HC*SUBMITTER ID*RECEIVER ID*20230516*1145*1*X*005010X222A1~
|
|
3
|
+
ST*837*0001*005010X222A1~
|
|
4
|
+
BHT*0019*00*244579*20230516*1145*CH~
|
|
5
|
+
NM1*41*2*SUBMIT CLINIC*****46*12345~
|
|
6
|
+
PER*IC*CONTACT NAME*TE*5555551234~
|
|
7
|
+
NM1*40*2*RECEIVER NAME*****46*67890~
|
|
8
|
+
HL*1**20*1~
|
|
9
|
+
NM1*85*2*BILLING PROVIDER*****XX*1234567893~
|
|
10
|
+
N3*123 BILLING ST~
|
|
11
|
+
N4*CITY*GA*30001~
|
|
12
|
+
REF*EI*123456789~
|
|
13
|
+
HL*2*1*22*0~
|
|
14
|
+
SBR*P*18*******MC~
|
|
15
|
+
NM1*IL*1*DOE*JOHN****MI*12345678901~
|
|
16
|
+
N3*123 PATIENT ST~
|
|
17
|
+
N4*CITY*GA*30001~
|
|
18
|
+
DMG*D8*19800101*M~
|
|
19
|
+
CLM*12345*150.00***11:B:1*Y*A*Y*Y~
|
|
20
|
+
HI*ABK:I109~
|
|
21
|
+
NM1*82*1*PROVIDER*JANE****XX*9876543210~
|
|
22
|
+
PRV*PE*ZZ*207RC0000X~
|
|
23
|
+
SV1*HC:J1745*150.00*UN*2*11***1~
|
|
24
|
+
DTP*472*D8*20230515~
|
|
25
|
+
LIN**N4*50242004001~
|
|
26
|
+
CTP***2*150.00~
|
|
27
|
+
SE*24*0001~
|
|
28
|
+
ST*837*5856*005010X223A2~
|
|
29
|
+
BHT*0019*00*241205204222*20241205*2042*CH~
|
|
30
|
+
NM1*41*2*HSA PORT ARTHUR, LLC*****XX*1194548073~
|
|
31
|
+
PER*IC*AIMEE GILL*TE*8186660602~
|
|
32
|
+
NM1*40*2*OptimaFourSight*****XX*89242VA018~
|
|
33
|
+
PER*IC*Jose Smith*TE*3750822093~
|
|
34
|
+
HL*1**20*1~
|
|
35
|
+
NM1*85*2*HSA PORT ARTHUR, LLC*****XX*1194548073~
|
|
36
|
+
N3*505 N BRAND BLVD STE 1200~
|
|
37
|
+
N4*GLENDALE*CA*91203~
|
|
38
|
+
REF*EI*71-3391736~
|
|
39
|
+
PER*IC*AIMEE GILL*TE*8186660602~
|
|
40
|
+
HL*2*1*22*0~
|
|
41
|
+
SBR*P*18*731323546******CI~
|
|
42
|
+
NM1*IL*1*Brad*Watson*D***MI*341405376684~
|
|
43
|
+
N3*2658 Edwards Lakes~
|
|
44
|
+
N4*Carterchester*AZ*20036~
|
|
45
|
+
CLM*4742333269*128***11:B:1*Y*A*Y*I~
|
|
46
|
+
DTP*434*RD8*20240422-20240430~
|
|
47
|
+
DTP*435*D8*20240809~
|
|
48
|
+
DTP*096*TM*2337~
|
|
49
|
+
HI*ABK:W214XXA~
|
|
50
|
+
HI*ABK:S31813D~
|
|
51
|
+
HI*ABK:V0492XD~
|
|
52
|
+
HI*ABK:T498X6A~
|
|
53
|
+
LX*1~
|
|
54
|
+
SV1*HC:37180*93*UN*1***3:4:1***~
|
|
55
|
+
DTP*472*D8*20180428~
|
|
56
|
+
REF*6R*142671~
|
|
57
|
+
LX*2~
|
|
58
|
+
SV1*HC:24000*4*UN*1***1:3:4***~
|
|
59
|
+
DTP*472*D8*20180428~
|
|
60
|
+
REF*6R*142671~
|
|
61
|
+
LX*3~
|
|
62
|
+
SV1*HC:16035*31*UN*1***3***~
|
|
63
|
+
DTP*472*D8*20180428~
|
|
64
|
+
REF*6R*142671~
|
|
65
|
+
SE*38*5856~
|
|
66
|
+
ST*837*4763033*005010X223A2~
|
|
67
|
+
BHT*0019*00*241205204221*20241205*2042*CH~
|
|
68
|
+
NM1*41*2*HCA HEALTH SERVICES OF TENNESSEE, INC.*****XX*1265487193~
|
|
69
|
+
PER*IC*DAVID SUMMERS*TE*6153421005~
|
|
70
|
+
NM1*40*2*HMOOffExchangeRegion7*****XX*84014CA002~
|
|
71
|
+
PER*IC*Devon Tran*TE*1808509992~
|
|
72
|
+
HL*1**20*1~
|
|
73
|
+
NM1*85*2*HCA HEALTH SERVICES OF TENNESSEE, INC.*****XX*1265487193~
|
|
74
|
+
N3*313 N MAIN ST~
|
|
75
|
+
N4*ASHLAND CITY*TN*37015~
|
|
76
|
+
REF*EI*99-5971744~
|
|
77
|
+
PER*IC*DAVID SUMMERS*TE*6153421005~
|
|
78
|
+
HL*2*1*22*0~
|
|
79
|
+
SBR*P*18*556791994******CI~
|
|
80
|
+
NM1*IL*1*Jessica*Rodriguez*G***MI*151319361359~
|
|
81
|
+
N3*3226 Andrew Point~
|
|
82
|
+
N4*North Brentville*IA*27129~
|
|
83
|
+
CLM*4742333269*839***11:B:1*Y*A*Y*I~
|
|
84
|
+
DTP*434*RD8*20240422-20240430~
|
|
85
|
+
DTP*435*D8*20240809~
|
|
86
|
+
DTP*096*TM*2337~
|
|
87
|
+
HI*ABK:V9421XS~
|
|
88
|
+
HI*ABK:S35292S~
|
|
89
|
+
HI*ABK:S52272S~
|
|
90
|
+
HI*ABK:H68022~
|
|
91
|
+
HI*ABK:T4144XD~
|
|
92
|
+
HI*ABK:H1030~
|
|
93
|
+
HI*ABK:S82832J~
|
|
94
|
+
HI*ABK:B340~
|
|
95
|
+
LX*1~
|
|
96
|
+
SV1*HC:35650*161*UN*1***7:3:4:5:8***~
|
|
97
|
+
DTP*472*D8*20180428~
|
|
98
|
+
REF*6R*142671~
|
|
99
|
+
LX*2~
|
|
100
|
+
SV1*HC:73200*383*UN*1***5:2:7:4:3:6:8:1***~
|
|
101
|
+
DTP*472*D8*20180428~
|
|
102
|
+
REF*6R*142671~
|
|
103
|
+
LX*3~
|
|
104
|
+
SV1*HC:28262*194*UN*1***7:1:8***~
|
|
105
|
+
DTP*472*D8*20180428~
|
|
106
|
+
REF*6R*142671~
|
|
107
|
+
LX*4~
|
|
108
|
+
SV1*HC:84480*101*UN*1***6:3:1:2***~
|
|
109
|
+
DTP*472*D8*20180428~
|
|
110
|
+
REF*6R*142671~
|
|
111
|
+
SE*46*4763033~
|
|
112
|
+
GE*1*1~
|
|
113
|
+
IEA*1*000000001~
|
hccinfhir/samples.py
CHANGED
|
@@ -43,7 +43,7 @@ class SampleData:
|
|
|
43
43
|
raise ValueError("case_number must be 1, 2, or 3")
|
|
44
44
|
|
|
45
45
|
try:
|
|
46
|
-
with importlib.resources.open_text('hccinfhir.
|
|
46
|
+
with importlib.resources.open_text('hccinfhir.sample_files', f'sample_eob_{case_number}.json') as f:
|
|
47
47
|
return json.load(f)
|
|
48
48
|
except FileNotFoundError:
|
|
49
49
|
raise FileNotFoundError(f"Sample EOB case {case_number} not found")
|
|
@@ -75,7 +75,7 @@ class SampleData:
|
|
|
75
75
|
"""
|
|
76
76
|
try:
|
|
77
77
|
output = []
|
|
78
|
-
with importlib.resources.open_text('hccinfhir.
|
|
78
|
+
with importlib.resources.open_text('hccinfhir.sample_files', 'sample_eob_200.ndjson') as f:
|
|
79
79
|
for i, line in enumerate(f):
|
|
80
80
|
if limit is not None and i >= limit:
|
|
81
81
|
break
|
|
@@ -91,13 +91,13 @@ class SampleData:
|
|
|
91
91
|
Retrieve a specific 837 claim sample by case number.
|
|
92
92
|
|
|
93
93
|
Args:
|
|
94
|
-
case_number: The case number (0 through
|
|
94
|
+
case_number: The case number (0 through 12). Default is 0.
|
|
95
95
|
|
|
96
96
|
Returns:
|
|
97
97
|
A string containing the 837 X12 claim data
|
|
98
98
|
|
|
99
99
|
Raises:
|
|
100
|
-
ValueError: If case_number is not between 0 and
|
|
100
|
+
ValueError: If case_number is not between 0 and 12
|
|
101
101
|
FileNotFoundError: If the sample file cannot be found
|
|
102
102
|
|
|
103
103
|
Example:
|
|
@@ -105,11 +105,11 @@ class SampleData:
|
|
|
105
105
|
>>> print("ISA" in sample_837)
|
|
106
106
|
True
|
|
107
107
|
"""
|
|
108
|
-
if case_number < 0 or case_number >
|
|
109
|
-
raise ValueError("case_number must be between 0 and
|
|
108
|
+
if case_number < 0 or case_number > 12:
|
|
109
|
+
raise ValueError("case_number must be between 0 and 12")
|
|
110
110
|
|
|
111
111
|
try:
|
|
112
|
-
with importlib.resources.open_text('hccinfhir.
|
|
112
|
+
with importlib.resources.open_text('hccinfhir.sample_files', f'sample_837_{case_number}.txt') as f:
|
|
113
113
|
return f.read()
|
|
114
114
|
except FileNotFoundError:
|
|
115
115
|
raise FileNotFoundError(f"Sample 837 case {case_number} not found")
|
|
@@ -138,20 +138,20 @@ class SampleData:
|
|
|
138
138
|
>>> # Get all samples
|
|
139
139
|
>>> all_samples = SampleData.get_837_sample_list()
|
|
140
140
|
>>> print(len(all_samples))
|
|
141
|
-
|
|
141
|
+
13
|
|
142
142
|
"""
|
|
143
143
|
if case_numbers is None:
|
|
144
|
-
case_numbers = list(range(
|
|
144
|
+
case_numbers = list(range(13)) # 0 through 12
|
|
145
145
|
|
|
146
146
|
# Validate case numbers
|
|
147
147
|
for case_num in case_numbers:
|
|
148
|
-
if case_num < 0 or case_num >
|
|
149
|
-
raise ValueError(f"case_number {case_num} must be between 0 and
|
|
148
|
+
if case_num < 0 or case_num > 12:
|
|
149
|
+
raise ValueError(f"case_number {case_num} must be between 0 and 12")
|
|
150
150
|
|
|
151
151
|
output = []
|
|
152
152
|
for case_num in case_numbers:
|
|
153
153
|
try:
|
|
154
|
-
with importlib.resources.open_text('hccinfhir.
|
|
154
|
+
with importlib.resources.open_text('hccinfhir.sample_files', f'sample_837_{case_num}.txt') as f:
|
|
155
155
|
output.append(f.read())
|
|
156
156
|
except FileNotFoundError:
|
|
157
157
|
raise FileNotFoundError(f"Sample 837 case {case_num} not found")
|
|
@@ -180,8 +180,8 @@ class SampleData:
|
|
|
180
180
|
],
|
|
181
181
|
"eob_case_numbers": [1, 2, 3],
|
|
182
182
|
"eob_list_size": 200,
|
|
183
|
-
"837_samples": [f"sample_837_{i}.txt" for i in range(
|
|
184
|
-
"837_case_numbers": list(range(
|
|
183
|
+
"837_samples": [f"sample_837_{i}.txt" for i in range(13)],
|
|
184
|
+
"837_case_numbers": list(range(13)),
|
|
185
185
|
"description": {
|
|
186
186
|
"eob": "Explanation of Benefits (FHIR resources) for testing HCC calculations",
|
|
187
187
|
"837": "X12 837 claim data for testing claim processing"
|
|
@@ -221,7 +221,7 @@ def get_837_sample(case_number: int = 0) -> str:
|
|
|
221
221
|
Convenience function to get an 837 claim sample.
|
|
222
222
|
|
|
223
223
|
Args:
|
|
224
|
-
case_number: The case number (0 through
|
|
224
|
+
case_number: The case number (0 through 12). Default is 0.
|
|
225
225
|
|
|
226
226
|
Returns:
|
|
227
227
|
A string containing the 837 X12 claim data
|