hccinfhir 0.1.0__py3-none-any.whl → 0.1.1__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/hccinfhir.py CHANGED
@@ -16,16 +16,16 @@ class HCCInFHIR:
16
16
  def __init__(self,
17
17
  filter_claims: bool = True,
18
18
  model_name: ModelName = "CMS-HCC Model V28",
19
- proc_filtering_filename: ProcFilteringFilename = "ra_eligible_cpt_hcpcs_2025.csv",
20
- dx_cc_mapping_filename: DxCCMappingFilename = "ra_dx_to_cc_2025.csv"):
19
+ proc_filtering_filename: ProcFilteringFilename = "ra_eligible_cpt_hcpcs_2026.csv",
20
+ dx_cc_mapping_filename: DxCCMappingFilename = "ra_dx_to_cc_2026.csv"):
21
21
  """
22
22
  Initialize the HCCInFHIR processor.
23
23
 
24
24
  Args:
25
25
  filter_claims: Whether to apply filtering rules to claims. Default is True.
26
26
  model_name: The name of the model to use for the calculation. Default is "CMS-HCC Model V28".
27
- proc_filtering_filename: The filename of the professional cpt filtering file. Default is "ra_eligible_cpt_hcpcs_2025.csv".
28
- dx_cc_mapping_filename: The filename of the dx to cc mapping file. Default is "ra_dx_to_cc_2025.csv".
27
+ proc_filtering_filename: The filename of the professional cpt filtering file. Default is "ra_eligible_cpt_hcpcs_2026.csv".
28
+ dx_cc_mapping_filename: The filename of the dx to cc mapping file. Default is "ra_dx_to_cc_2026.csv".
29
29
  """
30
30
  self.filter_claims = filter_claims
31
31
  self.model_name = model_name
@@ -8,7 +8,7 @@ from hccinfhir.model_interactions import apply_interactions
8
8
  from hccinfhir.utils import load_dx_to_cc_mapping, load_is_chronic
9
9
 
10
10
  # Load default mappings from csv file
11
- mapping_file_default = 'ra_dx_to_cc_2025.csv'
11
+ mapping_file_default = 'ra_dx_to_cc_2026.csv'
12
12
  dx_to_cc_default = load_dx_to_cc_mapping(mapping_file_default)
13
13
 
14
14
  # Load default mappings from csv file
@@ -87,8 +87,25 @@ def calculate_raf(diagnosis_codes: List[str],
87
87
  if is_chronic_mapping.get((hcc, model_name), False):
88
88
  hcc_chronic.add(hcc)
89
89
 
90
- coefficients_demographics = apply_coefficients(demographics, set(), {}, model_name)
91
- coefficients_chronic_only = apply_coefficients(demographics, hcc_chronic, {}, model_name)
90
+ demographic_interactions = {}
91
+ for key, value in interactions.items():
92
+ if key.startswith('NMCAID_'):
93
+ demographic_interactions[key] = value
94
+ elif key.startswith('MCAID_'):
95
+ demographic_interactions[key] = value
96
+ elif key.startswith('LTI_'):
97
+ demographic_interactions[key] = value
98
+ elif key.startswith('OriginallyDisabled_'):
99
+ demographic_interactions[key] = value
100
+
101
+ coefficients_demographics = apply_coefficients(demographics,
102
+ set(),
103
+ demographic_interactions,
104
+ model_name)
105
+ coefficients_chronic_only = apply_coefficients(demographics,
106
+ hcc_chronic,
107
+ demographic_interactions,
108
+ model_name)
92
109
 
93
110
  # Calculate risk scores
94
111
  risk_score = sum(coefficients.values())
@@ -3,7 +3,7 @@ import importlib.resources
3
3
  from hccinfhir.datamodels import ModelName, Demographics
4
4
 
5
5
  # Load default mappings from csv file
6
- coefficients_file_default = 'ra_coefficients_2025.csv'
6
+ coefficients_file_default = 'ra_coefficients_2026.csv'
7
7
  coefficients_default: Dict[Tuple[str, ModelName], float] = {} # (diagnosis_code, model_name) -> value
8
8
 
9
9
  try:
@@ -80,6 +80,7 @@ def get_coefficent_prefix(demographics: Demographics,
80
80
  return 'INS_'
81
81
 
82
82
  if demographics.new_enrollee:
83
+
83
84
  return 'SNPNE_' if demographics.snp else 'NE_'
84
85
 
85
86
  # Community case
@@ -139,5 +140,6 @@ def apply_coefficients(demographics: Demographics,
139
140
  value = coefficients[key]
140
141
  output[interaction_key] = value
141
142
 
143
+
142
144
  return output
143
145
 
@@ -57,6 +57,14 @@ def categorize_demographics(age: Union[int, float],
57
57
  raise ValueError("Sex must be 'M', 'F', '1', or '2'")
58
58
 
59
59
  # Determine if person is disabled or originally disabled
60
+ # SAS code:
61
+ # DISABL = (&AGEF < 65 & &OREC ne "0");
62
+ # ORIGDS = (&OREC = '1')*(DISABL = 0);
63
+ # The vairable names can be misleading.
64
+ # disabled is true if the person is disabled and the age is less than 65
65
+ # - basically, the person is in Medicare due to disability not due to age
66
+ # orig_disabled is true if the person started Medicare due to disability, but now aged in
67
+ # - basically, the person is in Medicare due to age (not disability anymore)
60
68
  disabled = age < 65 and (orec is not None and orec != "0")
61
69
  orig_disabled = (orec is not None and orec == '1') and not disabled
62
70
 
@@ -3,7 +3,7 @@ from hccinfhir.datamodels import ModelName
3
3
  from hccinfhir.utils import load_dx_to_cc_mapping
4
4
 
5
5
  # Load default mappings from csv file
6
- mapping_file_default = 'ra_dx_to_cc_2025.csv'
6
+ mapping_file_default = 'ra_dx_to_cc_2026.csv'
7
7
  dx_to_cc_default = load_dx_to_cc_mapping(mapping_file_default)
8
8
 
9
9
  def get_cc(
@@ -2,29 +2,33 @@ from typing import Dict, Set, Tuple
2
2
  import importlib.resources
3
3
  from hccinfhir.datamodels import ModelName
4
4
 
5
- # Load default mappings from csv file
6
- hierarchies_file_default = 'ra_hierarchies_2025.csv'
7
- hierarchies_default: Dict[Tuple[str, ModelName], Set[str]] = {} # (diagnosis_code, model_name) -> {cc}
5
+ def load_hierarchies(hierarchies_file: str) -> Dict[Tuple[str, ModelName], Set[str]]:
6
+ """Load hierarchies from a CSV file."""
7
+ hierarchies = {}
8
+ try:
9
+ with importlib.resources.open_text('hccinfhir.data', hierarchies_file) as f:
10
+ for line in f.readlines()[1:]: # Skip header
11
+ try:
12
+ cc_parent, cc_child, model_domain, model_version, _ = line.strip().split(',')
13
+ if model_domain == 'ESRD':
14
+ model_name = f"CMS-HCC {model_domain} Model {model_version}"
15
+ else:
16
+ model_name = f"{model_domain} Model {model_version}"
17
+ key = (cc_parent, model_name)
18
+ if key not in hierarchies:
19
+ hierarchies[key] = {cc_child}
20
+ else:
21
+ hierarchies[key].add(cc_child)
22
+ except ValueError:
23
+ continue # Skip malformed lines
24
+ except Exception as e:
25
+ print(f"Error loading mapping file: {e}")
26
+ hierarchies = {}
27
+ return hierarchies
8
28
 
9
- try:
10
- with importlib.resources.open_text('hccinfhir.data', hierarchies_file_default) as f:
11
- for line in f.readlines()[1:]: # Skip header
12
- try:
13
- cc_parent, cc_child, model_domain, model_version, _ = line.strip().split(',')
14
- if model_domain == 'ESRD':
15
- model_name = f"CMS-HCC {model_domain} Model {model_version}"
16
- else:
17
- model_name = f"{model_domain} Model {model_version}"
18
- key = (cc_parent, model_name)
19
- if key not in hierarchies_default:
20
- hierarchies_default[key] = {cc_child}
21
- else:
22
- hierarchies_default[key].add(cc_child)
23
- except ValueError:
24
- continue # Skip malformed lines
25
- except Exception as e:
26
- print(f"Error loading mapping file: {e}")
27
- hierarchies_default = {}
29
+ # Load default mappings from csv file
30
+ hierarchies_file_default = 'ra_hierarchies_2026.csv'
31
+ hierarchies_default: Dict[Tuple[str, ModelName], Set[str]] = load_hierarchies(hierarchies_file_default)
28
32
 
29
33
  def apply_hierarchies(
30
34
  cc_set: Set[str], # Set of active CCs
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: hccinfhir
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: HCC Algorithm for FHIR Resources
5
5
  Project-URL: Homepage, https://github.com/mimilabs/hccinfhir
6
6
  Project-URL: Issues, https://github.com/mimilabs/hccinfhir/issues
@@ -182,10 +182,12 @@ Each method returns a RAFResult containing:
182
182
  - Processed service level data (when applicable)
183
183
 
184
184
  ## Testing
185
+
186
+ After installing `hatch`
185
187
  ```bash
186
- $ python3 -m hatch shell
187
- $ python3 -m pip install -e .
188
- $ python3 -m pytest tests/*
188
+ $ hatch shell
189
+ $ pip install -e .
190
+ $ pytest tests/*
189
191
  ```
190
192
 
191
193
  ## Dependencies
@@ -267,36 +269,83 @@ $ python3 -m pytest tests/*
267
269
 
268
270
  ## Data Files
269
271
 
270
- `ra_dx_to_cc_mapping_2025.csv`
272
+ `ra_dx_to_cc_mapping_2026.csv`
271
273
  ```sql
272
- SELECT diagnosis_code, cc, model_name
273
- FROM ra_dx_to_cc_mapping
274
- WHERE year = 2025 and model_type = 'Initial';
274
+ WITH latest_years AS (
275
+ SELECT
276
+ model_name,
277
+ MAX(year) as latest_year
278
+ FROM mimi_ws_1.cmspayment.ra_dx_to_cc_mapping
279
+ WHERE model_type = 'Initial'
280
+ AND year <= 2026 -- Don't go beyond 2026
281
+ GROUP BY model_name
282
+ )
283
+ SELECT
284
+ r.diagnosis_code,
285
+ r.cc,
286
+ r.model_name
287
+ FROM mimi_ws_1.cmspayment.ra_dx_to_cc_mapping r
288
+ INNER JOIN latest_years l
289
+ ON r.model_name = l.model_name
290
+ AND r.year = l.latest_year
291
+ WHERE r.model_type = 'Initial'
292
+ ORDER BY r.model_name, r.diagnosis_code;
275
293
  ```
276
294
 
277
- `ra_hierarchies_2025.csv`
295
+ `ra_hierarchies_2026.csv`
278
296
  ```sql
279
- SELECT cc_parent,
280
- cc_child,
281
- model_domain,
282
- model_version,
283
- model_fullname
284
- FROM ra_hierarchies
285
- WHERE eff_last_date > '2025-01-01';
297
+ WITH latest_dates AS (
298
+ SELECT
299
+ model_domain,
300
+ model_version,
301
+ model_fullname,
302
+ MAX(eff_last_date) as latest_eff_last_date
303
+ FROM mimi_ws_1.cmspayment.ra_hierarchies
304
+ GROUP BY model_domain, model_version, model_fullname
305
+ )
306
+ SELECT
307
+ r.cc_parent,
308
+ r.cc_child,
309
+ r.model_domain,
310
+ r.model_version,
311
+ r.model_fullname
312
+ FROM mimi_ws_1.cmspayment.ra_hierarchies r
313
+ INNER JOIN latest_dates l
314
+ ON r.model_domain = l.model_domain
315
+ AND r.model_version = l.model_version
316
+ AND r.model_fullname = l.model_fullname
317
+ AND r.eff_last_date = l.latest_eff_last_date
318
+ ORDER BY r.model_domain, r.model_version, r.model_fullname, r.cc_parent, r.cc_child;
286
319
  ```
287
320
 
288
- `ra_coefficients_2025.csv`
321
+ `ra_coefficients_2026.csv`
289
322
  ```sql
290
- SELECT coefficient, value, model_domain, model_version
291
- FROM ra_coefficients
292
- WHERE eff_last_date > '2025-01-01';
323
+ WITH preferred_records AS (
324
+ SELECT
325
+ model_domain,
326
+ model_version,
327
+ MAX(eff_last_date) as latest_eff_last_date
328
+ FROM mimi_ws_1.cmspayment.ra_coefficients
329
+ GROUP BY model_domain, model_version
330
+ )
331
+ SELECT
332
+ r.coefficient,
333
+ r.value,
334
+ r.model_domain,
335
+ r.model_version
336
+ FROM mimi_ws_1.cmspayment.ra_coefficients r
337
+ INNER JOIN preferred_records p
338
+ ON r.model_domain = p.model_domain
339
+ AND r.model_version = p.model_version
340
+ AND r.eff_last_date = p.latest_eff_last_date
341
+ ORDER BY r.model_domain, r.model_version, r.coefficient;
293
342
  ```
294
343
 
295
- `ra_eligible_cpt_hcpcs_2025.csv`
344
+ `ra_eligible_cpt_hcpcs_2026.csv`
296
345
  ```sql
297
346
  SELECT DISTINCT cpt_hcpcs_code
298
347
  FROM mimi_ws_1.cmspayment.ra_eligible_cpt_hcpcs
299
- WHERE is_included = 'yes' AND YEAR(mimi_src_file_date) = 2024;
348
+ WHERE is_included = 'yes' AND YEAR(mimi_src_file_date) = 2025;
300
349
  ```
301
350
 
302
351
  ## Contributing
@@ -1,25 +1,29 @@
1
1
  hccinfhir/__init__.py,sha256=OCyYCv4jTOlYHZbTw2DTks3e6_YT1N2JXAOuyR03KNE,43
2
- hccinfhir/datamodels.py,sha256=2zdJf7oBF1j281bbi3hzIUWInUrWeU6IsC51EoV14Mo,4972
2
+ hccinfhir/datamodels.py,sha256=lMLGSuWTlpzoWenKsfhF8qQF3RZJV2NNGyIm_rBkd0o,5038
3
3
  hccinfhir/extractor.py,sha256=-jHVCIJqFAqvrI9GxkkXZVDQjKDa-7vF7v3PGMGAMnA,1801
4
4
  hccinfhir/extractor_837.py,sha256=vkTBCd0WBaJoTrUd-Z-zCIaoLk7KV2n4AGqIORhONIk,7147
5
5
  hccinfhir/extractor_fhir.py,sha256=Rg_L0Vg5tz_L2VJ_jvZwWz6RMlPAkHwj4LiK-OWQvrQ,8458
6
6
  hccinfhir/filter.py,sha256=8uYThN0-AqwVAKyti29WGiFwQKDiremMhYd_m6QcXhM,2193
7
- hccinfhir/hccinfhir.py,sha256=DxTkqbgVXsOsvX1M_UXemb68UMfC8oVCGus2CYIAiTM,6837
8
- hccinfhir/model_calculate.py,sha256=0IJctgfROAJR_3FU_x3VbX0oS3cyn29hFBzbkbEjoOs,4718
9
- hccinfhir/model_coefficients.py,sha256=UrDAEWBoqooSC8hy9YSUsLMmmfgIO0YGtVkui6ruOkE,5528
10
- hccinfhir/model_demographics.py,sha256=CMnD2-Au9Q6HNZrJUZqVVJRDEmSuK8ZpF39I22JkX8M,6564
11
- hccinfhir/model_dx_to_cc.py,sha256=jCFlnAOBkfI9FrCX6tZIh-Sp_DW0HwpY7QrPXGtwInI,1765
12
- hccinfhir/model_hierarchies.py,sha256=e8QtSayTrfPv2wh149FjK7ToiEmU1ISYMA1Pi38iVk0,2700
7
+ hccinfhir/hccinfhir.py,sha256=gVdQ6e1rJRVwIEcSw2cAUjnXH5WsCqaeDudn1Ol2_0w,6837
8
+ hccinfhir/model_calculate.py,sha256=3lKpNSdTNFn3OREw8yjlOhoNcDhs7LpQj7TIHQ1HvxQ,5519
9
+ hccinfhir/model_coefficients.py,sha256=ZsVY0S_X_BzDvcCmzCEf31v8uixbGmPAsR6nVEyCbIA,5530
10
+ hccinfhir/model_demographics.py,sha256=7W5NLW7aAjpn25BJzqfP8iSouD9uA6epGguJJ6BPuC0,7043
11
+ hccinfhir/model_dx_to_cc.py,sha256=guJny2Mb9z8YRNWCXGSIE3APbE06zwnA2NRkjAeUs60,1765
12
+ hccinfhir/model_hierarchies.py,sha256=0kdBmF_8e_ikMHBDhlw2I7jT3DupHfUn6o5mWj7v3Yo,2910
13
13
  hccinfhir/model_interactions.py,sha256=ZLiKJepPjPkYceKDf7dLXoYE0p44I7t9y2sTOlrxojo,20264
14
14
  hccinfhir/utils.py,sha256=AAHwzMSW8O9VZp1KLcdlN3OeBbxQtqQRtbTTdrKf7M0,2784
15
15
  hccinfhir/data/__init__.py,sha256=SGiSkpGrnxbvtEFMMlk82NFHOE50hFXcgKwKUSuVZUg,45
16
16
  hccinfhir/data/hcc_is_chronic.csv,sha256=eVVI4_8mQNkiBiNO3kattfT_zfcV18XgmiltdzZEXSo,17720
17
17
  hccinfhir/data/ra_coefficients_2025.csv,sha256=I0S2hoJlfig-D0oSFxy0b3Piv7m9AzOGo2CwR6bcQ9w,215191
18
+ hccinfhir/data/ra_coefficients_2026.csv,sha256=0gfjGgVdIEWkBO01NaAbTLMzHCYINA0rf_zl8ojngCY,288060
18
19
  hccinfhir/data/ra_dx_to_cc_2025.csv,sha256=4N7vF6VZndkl7d3Fo0cGsbAPAZdCjAizSH8BOKsZNAo,1618924
20
+ hccinfhir/data/ra_dx_to_cc_2026.csv,sha256=YT9HwQFUddL_bxuE9nxHWsBtZzojINL0DzABBMp6kms,1751007
19
21
  hccinfhir/data/ra_eligible_cpt_hcpcs_2023.csv,sha256=VVoA4s0hsFmcRIugyFdbvSoeLcn7M7z0DITT6l4YqL8,39885
20
22
  hccinfhir/data/ra_eligible_cpt_hcpcs_2024.csv,sha256=CawKImfCb8fFMDbWwqvNLRyRAda_u9N8Q3ne8QAAe54,40191
21
23
  hccinfhir/data/ra_eligible_cpt_hcpcs_2025.csv,sha256=-tMvv2su5tsSbGUh6fZZCMUEkXInBpcTtbUCi2o_UwI,40359
24
+ hccinfhir/data/ra_eligible_cpt_hcpcs_2026.csv,sha256=1oXFcLJQPF_fbGd1sLZU6IPolKCXOculovqIUAoMgLc,40359
22
25
  hccinfhir/data/ra_hierarchies_2025.csv,sha256=HQSPNloe6mvvwMgv8ZwYAfWKkT2b2eUvm4JQy6S_mVQ,13045
26
+ hccinfhir/data/ra_hierarchies_2026.csv,sha256=Q7xi5PpgJ4LETadup4vKoKud9ZWeu2OZVpwgr4VLpMw,19248
23
27
  hccinfhir/samples/__init__.py,sha256=SGiSkpGrnxbvtEFMMlk82NFHOE50hFXcgKwKUSuVZUg,45
24
28
  hccinfhir/samples/sample_837_0.txt,sha256=eggrD259uHa05z2dfxWBpUDseSDp_AQcLyN_adpHyTw,5295
25
29
  hccinfhir/samples/sample_837_1.txt,sha256=E155MdemSDYoXokuTXUZ6Br_RGGedYv5t5dh-eMRmuk,1322
@@ -37,7 +41,7 @@ hccinfhir/samples/sample_eob_1.json,sha256=_NGSVR2ysFpx-DcTvyga6dFCzhQ8Vi9fNzQEM
37
41
  hccinfhir/samples/sample_eob_2.json,sha256=FcnJcx0ApOczxjJ_uxVLzCep9THfNf4xs9Yf7hxk8e4,1769
38
42
  hccinfhir/samples/sample_eob_200.ndjson,sha256=CxpjeQ1DCMUzZILaM68UEhfxO0p45YGhDDoCZeq8PxU,1917986
39
43
  hccinfhir/samples/sample_eob_3.json,sha256=4BW4wOMBEEU9RDfJR15rBEvk0KNHyuMEh3e055y87Hc,2306
40
- hccinfhir-0.1.0.dist-info/METADATA,sha256=iao0WVikTc_19EBBI3QuSPrO5KSffu_IZFBoiYZkcUs,11567
41
- hccinfhir-0.1.0.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
42
- hccinfhir-0.1.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
43
- hccinfhir-0.1.0.dist-info/RECORD,,
44
+ hccinfhir-0.1.1.dist-info/METADATA,sha256=t5eApNi_1F-OCQiB16B2W6AUxS-mPpRTni6swrLqbRg,12881
45
+ hccinfhir-0.1.1.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
46
+ hccinfhir-0.1.1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
47
+ hccinfhir-0.1.1.dist-info/RECORD,,