Merged in code-breakout-mismaps (pull request #419)
DAIP2-112 - fixed get code descriptions * indirect proc codes added * bug fix * bug fix * Merge branch 'main' into proc-code-indirect * description mapping handling X's and 0's * bug fix * Merge remote-tracking branch 'origin/proc-code-indirect' into code-breakout-mismaps * testing mismaps * revenue codes mapping for int values * added function to find ranges * removed test files * Merged main into code-breakout-mismaps * Merge branch 'main' into code-breakout-mismaps pulled latest changes from main * added unit test for find containing ranges * modified find_containing_ranges_test file * Merged main into code-breakout-mismaps * added embeddings to git ignore * updated find_containing_ranges_test.py Approved-by: Alex Galarce Approved-by: Katon Minhas
This commit is contained in:
committed by
Alex Galarce
parent
0fee01ace8
commit
576ea95b6d
@@ -95,3 +95,6 @@ __pycache__/
|
|||||||
|
|
||||||
.env
|
.env
|
||||||
new/
|
new/
|
||||||
|
|
||||||
|
# Embeddings
|
||||||
|
fieldExtraction/embeddings/
|
||||||
@@ -78,6 +78,60 @@ def crosswalk_levels():
|
|||||||
|
|
||||||
return crosswalk_dict
|
return crosswalk_dict
|
||||||
|
|
||||||
|
def find_containing_ranges(input_range, code_mapping):
|
||||||
|
"""
|
||||||
|
Find which code ranges in the dictionary contain or overlap with the input range.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
input_range (str): Code range in format 'START-END' (e.g., '70010-79999')
|
||||||
|
code_mapping (dict): Dictionary containing CPT codes with code ranges as keys and descriptions as values
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list: List of dictionaries containing matching code ranges and descriptions
|
||||||
|
"""
|
||||||
|
# Parse the input range
|
||||||
|
match = re.match(r'(\d+)-(\d+)', input_range)
|
||||||
|
if not match:
|
||||||
|
raise ValueError("Invalid input format. Expected format: 'START-END'")
|
||||||
|
|
||||||
|
input_start = int(match.group(1))
|
||||||
|
input_end = int(match.group(2))
|
||||||
|
|
||||||
|
# Initialize results list
|
||||||
|
results = []
|
||||||
|
|
||||||
|
# Process each code range in the dictionary
|
||||||
|
for code_range, description in code_mapping.items():
|
||||||
|
# Parse the code range from the dictionary
|
||||||
|
range_match = re.match(r'(\d+)-(\d+)', code_range)
|
||||||
|
if range_match:
|
||||||
|
csv_start = int(range_match.group(1))
|
||||||
|
csv_end = int(range_match.group(2))
|
||||||
|
|
||||||
|
# Check if there's any overlap between ranges
|
||||||
|
if max(input_start, csv_start) <= min(input_end, csv_end):
|
||||||
|
# The ranges overlap in some way
|
||||||
|
results.append({
|
||||||
|
'code_range': code_range,
|
||||||
|
'description': description,
|
||||||
|
'overlap_start': max(input_start, csv_start),
|
||||||
|
'overlap_end': min(input_end, csv_end)
|
||||||
|
})
|
||||||
|
# Extract only the code_range values
|
||||||
|
# code_ranges = [result['code_range'] for result in results]
|
||||||
|
# If no single range contains all others, return the highest level ranges
|
||||||
|
highest_level_ranges = []
|
||||||
|
for result in results:
|
||||||
|
is_highest = True
|
||||||
|
for other in results:
|
||||||
|
if result != other and int(result['overlap_start']) >= int(other['overlap_start']) and int(result['overlap_end']) <= int(other['overlap_end']):
|
||||||
|
is_highest = False
|
||||||
|
break
|
||||||
|
if is_highest:
|
||||||
|
highest_level_ranges.append(result['code_range'])
|
||||||
|
return highest_level_ranges
|
||||||
|
# return code_ranges
|
||||||
|
|
||||||
def code_primary(answer_dicts, filename):
|
def code_primary(answer_dicts, filename):
|
||||||
"""
|
"""
|
||||||
Breaks down the direct code information from CONTRACT_SERVICE_CD_OR_DESC and CONTRACT_REIMBURSEMENT_METHOD fields
|
Breaks down the direct code information from CONTRACT_SERVICE_CD_OR_DESC and CONTRACT_REIMBURSEMENT_METHOD fields
|
||||||
@@ -266,8 +320,10 @@ def get_code_description(code, code_mapping):
|
|||||||
code_list = ast.literal_eval(code)
|
code_list = ast.literal_eval(code)
|
||||||
elif "," in code:
|
elif "," in code:
|
||||||
code_list = code.split(",")
|
code_list = code.split(",")
|
||||||
|
elif "-" in code:
|
||||||
|
code_list = find_containing_ranges(code, code_mapping.mapping)
|
||||||
else:
|
else:
|
||||||
code_list = [code]
|
code_list = [code] # added this line to handle range strings like 'J0120-J7175'
|
||||||
elif isinstance(code, list):
|
elif isinstance(code, list):
|
||||||
code_list = code
|
code_list = code
|
||||||
else:
|
else:
|
||||||
@@ -279,6 +335,27 @@ def get_code_description(code, code_mapping):
|
|||||||
description = code_mapping.mapping.get(code_str, "mismap")
|
description = code_mapping.mapping.get(code_str, "mismap")
|
||||||
|
|
||||||
if string_utils.is_empty(description) or description == "mismap":
|
if string_utils.is_empty(description) or description == "mismap":
|
||||||
|
if code_str.isdigit():
|
||||||
|
description = code_mapping.mapping.get(int(code_str), "mismap")
|
||||||
|
else:
|
||||||
|
description = "mismap"
|
||||||
|
|
||||||
|
# If mismap, try to handle ranges
|
||||||
|
if string_utils.is_empty(description) or description == "mismap":
|
||||||
|
if "X" in code_str:
|
||||||
|
base_code = code_str.replace("X", "")
|
||||||
|
expanded_code_range = f"{base_code}0-{base_code}9"
|
||||||
|
description = code_mapping.mapping.get(expanded_code_range, "mismap")
|
||||||
|
# If mismap, try to handle numeric codes
|
||||||
|
if string_utils.is_empty(description) or description == "mismap":
|
||||||
|
numeric_codes_description = []
|
||||||
|
for i in range(10):
|
||||||
|
expanded_code = base_code + str(i)
|
||||||
|
inner_description = code_mapping.mapping.get(expanded_code, "mismap")
|
||||||
|
if inner_description != "mismap":
|
||||||
|
numeric_codes_description.append(inner_description)
|
||||||
|
description = ", ".join(numeric_codes_description)
|
||||||
|
else:
|
||||||
# If mismap, prepend "0" and try again (useful for rev codes)
|
# If mismap, prepend "0" and try again (useful for rev codes)
|
||||||
code_str = "0" + code_str
|
code_str = "0" + code_str
|
||||||
description = code_mapping.mapping.get(code_str, "mismap")
|
description = code_mapping.mapping.get(code_str, "mismap")
|
||||||
@@ -290,6 +367,12 @@ def get_code_description(code, code_mapping):
|
|||||||
if not string_utils.is_empty(description):
|
if not string_utils.is_empty(description):
|
||||||
code_description.append(description)
|
code_description.append(description)
|
||||||
|
|
||||||
|
# Remove repeated values and retain unique values, excluding "mismap" if other values are present
|
||||||
|
unique_descriptions = list(set(code_description))
|
||||||
|
if "mismap" in unique_descriptions and len(unique_descriptions) > 1:
|
||||||
|
unique_descriptions.remove("mismap")
|
||||||
|
code_description = unique_descriptions
|
||||||
|
|
||||||
return ",".join(code_description)
|
return ",".join(code_description)
|
||||||
|
|
||||||
def get_mappings():
|
def get_mappings():
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import pytest
|
||||||
|
from unittest.mock import patch, MagicMock
|
||||||
|
from src.investment.code_funcs import find_containing_ranges
|
||||||
|
|
||||||
|
def test_find_containing_ranges_valid_input():
|
||||||
|
input_range = '70010-79999'
|
||||||
|
code_mapping = {
|
||||||
|
'70000-70050': 'Description 1',
|
||||||
|
'70040-70060': 'Description 2',
|
||||||
|
'70010-70020': 'Description 3',
|
||||||
|
'80000-80050': 'Description 4'
|
||||||
|
}
|
||||||
|
expected_output = ['70000-70050', '70040-70060']
|
||||||
|
assert find_containing_ranges(input_range, code_mapping) == expected_output
|
||||||
|
|
||||||
|
def test_find_containing_ranges_no_overlap():
|
||||||
|
input_range = '70010-70020'
|
||||||
|
code_mapping = {
|
||||||
|
'70030-70040': 'Description 1',
|
||||||
|
'70050-70060': 'Description 2',
|
||||||
|
'80000-80050': 'Description 3'
|
||||||
|
}
|
||||||
|
expected_output = []
|
||||||
|
assert find_containing_ranges(input_range, code_mapping) == expected_output
|
||||||
|
|
||||||
|
def test_find_containing_ranges_partial_overlap():
|
||||||
|
input_range = '70010-70050'
|
||||||
|
code_mapping = {
|
||||||
|
'70000-70020': 'Description 1',
|
||||||
|
'70040-70060': 'Description 2',
|
||||||
|
'70050-70070': 'Description 3'
|
||||||
|
}
|
||||||
|
expected_output = ['70000-70020', '70040-70060']
|
||||||
|
assert find_containing_ranges(input_range, code_mapping) == expected_output
|
||||||
|
|
||||||
|
def test_find_containing_ranges_exact_match():
|
||||||
|
input_range = '70000-70050'
|
||||||
|
code_mapping = {
|
||||||
|
'70000-70050': 'Description 1',
|
||||||
|
'70040-70060': 'Description 2',
|
||||||
|
'70010-70020': 'Description 3'
|
||||||
|
}
|
||||||
|
expected_output = ['70000-70050']
|
||||||
|
assert find_containing_ranges(input_range, code_mapping) == expected_output
|
||||||
|
|
||||||
|
def test_find_containing_ranges_empty_mapping():
|
||||||
|
input_range = '70000-70050'
|
||||||
|
code_mapping = {}
|
||||||
|
expected_output = []
|
||||||
|
assert find_containing_ranges(input_range, code_mapping) == expected_output
|
||||||
|
|
||||||
|
def test_find_containing_ranges_invalid_input():
|
||||||
|
input_range = 'invalid-range'
|
||||||
|
code_mapping = {
|
||||||
|
'70000-70050': 'Description 1',
|
||||||
|
'70040-70060': 'Description 2'
|
||||||
|
}
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
find_containing_ranges(input_range, code_mapping)
|
||||||
Reference in New Issue
Block a user