Merged in feature/lesser-table-caching-refactor-hybrid (pull request #847)
Feature/lesser table caching refactor hybrid * chore: Remove unused duplicate main.py from shared pipeline * fix: Correct crosswalk paths in aarete_derived.py * chore: Remove unused documentation files from fieldExtraction * docs: Add documentation files to documentation folder * docs: Update README with uv setup, expanded project structure, and branching conventions * docs: Add uv installation steps with Ubuntu/WSL emphasis * Enable prompt caching for all remaining LLM calls - Add _INSTRUCTION() functions for: EXHIBIT_HEADER, EXHIBIT_LINKAGE, EXHIBIT_TITLE_MATCH, DATE_FIX, DERIVED_TERM_DATE, CHECK_PROVIDER_NAME_MATCH, SPECIAL_CASE_ASSIGNMENT - Update all invoke_claude() calls in saas and clover pipelines to use cache=True with corresponding _INSTRUCTION() functions - Add new instructions to get_cacheable_instructions() for cache warming - Update tests for new instruction functions Functions now using caching: - prompt_exhibit_level - prompt_exhibit_lesser (EXHIBIT_LEVEL_LESSER_OF) - prompt_fee_schedule_breakout - prompt_grouper_breakout - prompt_special_case_assignment - prompt_exhibit_linkage - prompt_exhibit_header - prompt_smart_chunked (ONE_TO_ONE templates) - prompt_date_fix - prompt_derived_term_date - prompt_exhibit_title_match - provider_name_match_check 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Reorder * feat: Add bcbs_promise client pipeline with OFFSET_TERM extraction - Add new bcbs_promise client with HSC-based OFFSET_TERM field extraction - Extract full paragraph text of offset/recoupment provisions from contracts - Derive OFFSET_INDICATOR (Y/N) from OFFSET_TERM presence - Fix reorder_columns to preserve extra columns not in COLUMN_ORDER - Update QC/QA output path to outputs/qc_qa/ * fix: Update dev deps and test assertions for QC/QA output path - Add pytest/pytest-mock to dev dependencies for mypy type checking - Update test assertions to expect outputs/qc_qa instead of qa_qc_output * style: Apply black formatting to prompt_templates.py * Merge main, move scripts * Archive some scripts * update py version * remove .py version file * Remove ASCII characters * Restore testbed code * restore tracking * Update testbed metrics * Enable prompt caching for CODE_LAST_CHECK, FILL_BILL_TYPE, DUAL_LOB_CHECK, and GROUPER_BREAKOUT - Add CODE_LAST_CHECK_INSTRUCTION() for service specificity classification - Add FILL_BILL_TYPE_INSTRUCTION() for bill type code determination - Add DUAL_LOB_CHECK_INSTRUCTION() for Medicare/Medicaid classification - Update code_funcs.py to use caching for CODE_LAST_CHECK, FILL_BILL_TYPE, GROUPER_BREAKOUT - Update postprocessing_funcs.py to use caching for DUAL_LOB_CHECK - Add new instructions to get_cacheable_instructions() for cache warming - Add unit tests for new instruction functions 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Fix postprocessing_funcs to remove invalid columns * Merge branch 'main' into feature/lesser-table-caching-refactor-hybrid * Revert prompt caching changes from aed1b73c * update formatting * Update imports Approved-by: Sha Brown Approved-by: Praneel Panchigar
This commit is contained in:
@@ -0,0 +1,454 @@
|
||||
import json
|
||||
import re
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pandas as pd
|
||||
import src.codes.code_funcs as code_funcs
|
||||
from src.constants.constants import Constants
|
||||
from src.constants.delimiters import Delimiter
|
||||
|
||||
|
||||
class TestCodeFuncs(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test fixtures before each test method."""
|
||||
# Create a mock Constants object
|
||||
self.constants = MagicMock(spec=Constants)
|
||||
|
||||
# Configure mock constants
|
||||
self.constants.SYNONYM_MAP = {
|
||||
"DME": "DURABLE MEDICAL EQUIPMENT",
|
||||
"SNF": "SKILLED NURSING FACILITY",
|
||||
}
|
||||
self.constants.REMOVAL_LIST = ["INPATIENT", "OUTPATIENT", "SERVICES"]
|
||||
self.constants.STOP_WORD_LIST = ["THE", "AND", "OF"]
|
||||
self.constants.DO_NOT_RUN = ["BY REPORT", "STOP-LOSS"]
|
||||
|
||||
# Setup mappings
|
||||
self.constants.CPT_LEVEL1_MAPPING = {"12345": "Test Procedure"}
|
||||
self.constants.HCPCS_LEVEL1_MAPPING = {"J0001": "Test Drug"}
|
||||
self.constants.REV_LEVEL1_MAPPING = {"123": "Test Revenue"}
|
||||
|
||||
self.constants.CPT_LEVEL2_MAPPING = {"67890": "Advanced Procedure"}
|
||||
self.constants.HCPCS_LEVEL2_MAPPING = {"J0002": "Advanced Drug"}
|
||||
self.constants.REV_MAPPING = {"456": "Advanced Revenue"}
|
||||
|
||||
self.constants.BILL_TYPE_MAPPING = {"11X": "Inpatient Hospital"}
|
||||
self.constants.BILL_TYPE_REVERSE_MAPPING = {"Inpatient Hospital": "11X"}
|
||||
|
||||
# Mock the embedding model and function
|
||||
mock_embedding = MagicMock()
|
||||
mock_embedding.index.search.return_value = ([0.9], [[0]])
|
||||
mock_embedding.choices = ["Test Description"]
|
||||
self.constants.get_embedding.return_value = mock_embedding
|
||||
|
||||
self.constants.EMBEDDING_MODEL = MagicMock()
|
||||
mock_encoded = MagicMock()
|
||||
mock_encoded.astype.return_value = "mock_vector"
|
||||
self.constants.EMBEDDING_MODEL.encode.return_value = mock_encoded
|
||||
|
||||
def test_clean_service(self):
|
||||
"""Test the clean_service function with various inputs."""
|
||||
# Test empty input
|
||||
self.assertEqual(code_funcs.clean_service("", self.constants), "")
|
||||
self.assertEqual(code_funcs.clean_service(None, self.constants), "")
|
||||
|
||||
# Test synonym mapping
|
||||
self.assertEqual(
|
||||
code_funcs.clean_service("DME", self.constants), "DURABLE MEDICAL EQUIPMENT"
|
||||
)
|
||||
|
||||
# Test removal list
|
||||
self.assertEqual(
|
||||
code_funcs.clean_service("INPATIENT SERVICES", self.constants), ""
|
||||
)
|
||||
|
||||
# Test stop words only
|
||||
self.assertEqual(code_funcs.clean_service("THE AND OF", self.constants), "")
|
||||
|
||||
# Test unlisted service
|
||||
self.assertEqual(
|
||||
code_funcs.clean_service("UNLISTED PROCEDURE", self.constants), "UNLISTED"
|
||||
)
|
||||
|
||||
# Test normal case
|
||||
self.assertEqual(
|
||||
code_funcs.clean_service("CT SCAN SNF", self.constants),
|
||||
"CT SCAN SKILLED NURSING FACILITY",
|
||||
)
|
||||
|
||||
@patch("src.utils.llm_utils.invoke_claude")
|
||||
@patch("src.utils.string_utils.universal_json_load")
|
||||
def test_code_explicit(self, mock_json_load, mock_invoke_claude):
|
||||
"""Test the code_explicit function for extracting explicit codes."""
|
||||
# Setup mocks
|
||||
mock_invoke_claude.return_value = '{"PROCEDURE_CD": "12345"}'
|
||||
mock_json_load.return_value = {"PROCEDURE_CD": "12345"}
|
||||
|
||||
# Test successful extraction
|
||||
result = code_funcs.code_explicit(
|
||||
"TEST SERVICE", "TEST_METHODOLOGY", "test.pdf"
|
||||
)
|
||||
self.assertEqual(result, {"PROCEDURE_CD": "12345"})
|
||||
mock_invoke_claude.assert_called_once()
|
||||
|
||||
@patch("src.utils.llm_utils.invoke_claude")
|
||||
@patch("src.utils.string_utils.universal_json_load")
|
||||
def test_code_category(self, mock_json_load, mock_invoke_claude):
|
||||
"""Test the code_category function for detecting code categories."""
|
||||
# Setup mocks
|
||||
mock_invoke_claude.return_value = '["Drug A", "Drug B"]'
|
||||
mock_json_load.return_value = ["Drug A", "Drug B"]
|
||||
|
||||
# Setup test data
|
||||
proc_category = ["Category: J"]
|
||||
hcpcs_mapping = {"J0001": "Drug A", "J0002": "Drug B", "K0001": "Other Drug"}
|
||||
|
||||
# Test category matching
|
||||
result = code_funcs.code_category(
|
||||
"J CODES", proc_category, hcpcs_mapping, "test.pdf"
|
||||
)
|
||||
|
||||
# Verify the results - code_category returns lists (JSON conversion happens in extract_codes_from_service)
|
||||
self.assertEqual(result["PROCEDURE_CD"], ["J0001", "J0002"])
|
||||
self.assertEqual(result["PROCEDURE_CD_DESC"], ["Drug A", "Drug B"])
|
||||
self.assertIn("J0001", result["PROCEDURE_CD"])
|
||||
self.assertIn("J0002", result["PROCEDURE_CD"])
|
||||
self.assertIn("Drug A", result["PROCEDURE_CD_DESC"])
|
||||
self.assertIn("Drug B", result["PROCEDURE_CD_DESC"])
|
||||
|
||||
@patch("src.utils.llm_utils.invoke_claude")
|
||||
@patch("src.utils.string_utils.extract_text_from_delimiters")
|
||||
def test_code_implicit_special(self, mock_extract, mock_invoke_claude):
|
||||
"""Test the code_implicit_special function for special case handling."""
|
||||
# Setup mocks
|
||||
mock_invoke_claude.return_value = "mock_response"
|
||||
mock_extract.return_value = "Drugs"
|
||||
|
||||
# Test drug category - returns a list
|
||||
result = code_funcs.code_implicit_special("DRUG SERVICE", "test.pdf")
|
||||
self.assertEqual(result["PROCEDURE_CD"], ["J0000-J9999"])
|
||||
self.assertEqual(result["PROCEDURE_CD_DESC"], "Drugs")
|
||||
|
||||
# Test no match
|
||||
mock_extract.return_value = ""
|
||||
result = code_funcs.code_implicit_special("OTHER SERVICE", "test.pdf")
|
||||
self.assertEqual(result, {})
|
||||
|
||||
def test_get_embedding_levels(self):
|
||||
"""Test the get_embedding_levels function for determining embedding levels."""
|
||||
# Test level 1 with both codes enabled
|
||||
implicit_run_dict = {"PROCEDURE_CD": True, "REVENUE_CD": True}
|
||||
embedding_levels, cpt_mapping, hcpcs_mapping, rev_mapping = (
|
||||
code_funcs.get_embedding_levels(1, implicit_run_dict, self.constants)
|
||||
)
|
||||
|
||||
self.assertIn("cpt_level1", embedding_levels)
|
||||
self.assertIn("hcpcs_level1", embedding_levels)
|
||||
self.assertIn("rev_level1", embedding_levels)
|
||||
self.assertEqual(cpt_mapping, self.constants.CPT_LEVEL1_MAPPING)
|
||||
|
||||
# Test level 2 with only procedure code enabled
|
||||
implicit_run_dict = {"PROCEDURE_CD": True, "REVENUE_CD": False}
|
||||
embedding_levels, cpt_mapping, hcpcs_mapping, rev_mapping = (
|
||||
code_funcs.get_embedding_levels(2, implicit_run_dict, self.constants)
|
||||
)
|
||||
|
||||
self.assertIn("cpt_level2", embedding_levels)
|
||||
self.assertIn("hcpcs_level2", embedding_levels)
|
||||
self.assertNotIn("rev", embedding_levels)
|
||||
self.assertEqual(cpt_mapping, self.constants.CPT_LEVEL2_MAPPING)
|
||||
|
||||
def test_get_match_list(self):
|
||||
"""Test the get_match_list function for retrieving matches from embeddings."""
|
||||
# Configure mock for proper array-like return values
|
||||
mock_embedding = MagicMock()
|
||||
# Search returns a tuple of (scores, indices) where each is a 2D array
|
||||
mock_embedding.index.search.return_value = (
|
||||
# Scores array - 2D array with shape [1, top_k]
|
||||
[[0.9]],
|
||||
# Indices array - 2D array with shape [1, top_k]
|
||||
[[0]],
|
||||
)
|
||||
mock_embedding.choices = ["Test Description"]
|
||||
|
||||
# Replace the mock embedding in constants for this test
|
||||
self.constants.get_embedding.return_value = mock_embedding
|
||||
|
||||
# Test with constants mock configured in setUp
|
||||
match_list, highest_similarity = code_funcs.get_match_list(
|
||||
"TEST SERVICE", ["cpt_level1"], self.constants, 1
|
||||
)
|
||||
|
||||
# Verify results
|
||||
self.assertEqual(match_list, ["Test Description"])
|
||||
self.assertEqual(highest_similarity, 0.9)
|
||||
|
||||
# Verify the appropriate method calls
|
||||
self.constants.EMBEDDING_MODEL.encode.assert_called_once()
|
||||
self.constants.get_embedding.assert_called_once_with("cpt_level1")
|
||||
|
||||
@patch("src.utils.llm_utils.invoke_claude")
|
||||
@patch("src.utils.string_utils.universal_json_load")
|
||||
@patch("src.codes.code_funcs.get_match_list")
|
||||
def test_code_implicit_rag(
|
||||
self, mock_get_match_list, mock_json_load, mock_invoke_claude
|
||||
):
|
||||
"""Test the code_implicit_rag function for RAG-based code extraction."""
|
||||
# Mock get_match_list to return properly formatted results
|
||||
mock_get_match_list.return_value = (["Test Procedure"], 0.9)
|
||||
|
||||
# Setup mocks for Claude responses
|
||||
mock_invoke_claude.return_value = '["Test Procedure"]'
|
||||
mock_json_load.return_value = ["Test Procedure"]
|
||||
|
||||
# Test with successful match
|
||||
implicit_run_dict = {"PROCEDURE_CD": True, "REVENUE_CD": True}
|
||||
|
||||
# Patch the get_embedding_levels function to return predictable values
|
||||
with patch(
|
||||
"src.codes.code_funcs.get_embedding_levels"
|
||||
) as mock_get_embedding_levels:
|
||||
# Configure mock to return appropriate values for embedding levels
|
||||
mock_get_embedding_levels.return_value = (
|
||||
["cpt_level1"],
|
||||
{"12345": "Test Procedure"},
|
||||
{},
|
||||
{},
|
||||
)
|
||||
|
||||
# Call the function
|
||||
result = code_funcs.code_implicit_rag(
|
||||
"TEST SERVICE", implicit_run_dict, "test.pdf", self.constants
|
||||
)
|
||||
|
||||
# Verify results - code_implicit_rag returns lists
|
||||
self.assertEqual(result["PROCEDURE_CD"], ["12345"])
|
||||
self.assertEqual(result["PROCEDURE_CD_DESC"], ["Test Procedure"])
|
||||
self.assertEqual(result["CODE_METHODOLOGY"], "Implicit - Level 1")
|
||||
|
||||
# Test exception handling
|
||||
mock_json_load.side_effect = Exception("Test error")
|
||||
result = code_funcs.code_implicit_rag(
|
||||
"TEST SERVICE", implicit_run_dict, "test.pdf", self.constants
|
||||
)
|
||||
self.assertIn("CODE_METHODOLOGY", result)
|
||||
|
||||
@patch("src.utils.llm_utils.invoke_claude")
|
||||
@patch("src.utils.string_utils.extract_text_from_delimiters")
|
||||
def test_code_last_check(self, mock_extract, mock_invoke_claude):
|
||||
"""Test the code_last_check function for categorizing non-matched services."""
|
||||
# Setup mocks
|
||||
mock_invoke_claude.return_value = "mock_response"
|
||||
|
||||
# Test specific case
|
||||
mock_extract.return_value = "Specific"
|
||||
result = code_funcs.code_last_check("SPECIAL SERVICE", "test.pdf")
|
||||
self.assertEqual(result, "Specific")
|
||||
|
||||
# Test generic case
|
||||
mock_extract.return_value = "Generic"
|
||||
result = code_funcs.code_last_check("GENERIC SERVICE", "test.pdf")
|
||||
self.assertEqual(result, "Generic")
|
||||
|
||||
@patch("src.utils.llm_utils.invoke_claude")
|
||||
@patch("src.utils.string_utils.universal_json_load")
|
||||
def test_fill_bill_type(self, mock_json_load, mock_invoke_claude):
|
||||
"""Test the fill_bill_type function for populating bill type information."""
|
||||
# Setup mocks
|
||||
mock_invoke_claude.return_value = '["Inpatient Hospital"]'
|
||||
mock_json_load.return_value = ["Inpatient Hospital"]
|
||||
|
||||
# Test with valid bill type
|
||||
answer_dict = {}
|
||||
result = code_funcs.fill_bill_type(
|
||||
"INPATIENT SERVICE",
|
||||
answer_dict,
|
||||
self.constants.BILL_TYPE_MAPPING,
|
||||
self.constants.BILL_TYPE_REVERSE_MAPPING,
|
||||
)
|
||||
|
||||
self.assertEqual(result["BILL_TYPE_CD"], "11X")
|
||||
self.assertEqual(result["BILL_TYPE_CD_DESC"], "Inpatient Hospital")
|
||||
|
||||
# Test with empty response
|
||||
mock_json_load.return_value = []
|
||||
answer_dict = {"SERVICE_TERM": "Test"}
|
||||
result = code_funcs.fill_bill_type(
|
||||
"OTHER SERVICE",
|
||||
answer_dict,
|
||||
self.constants.BILL_TYPE_MAPPING,
|
||||
self.constants.BILL_TYPE_REVERSE_MAPPING,
|
||||
)
|
||||
|
||||
self.assertEqual(result, {"SERVICE_TERM": "Test"})
|
||||
|
||||
def test_get_implicit_runs(self):
|
||||
"""Test the get_implicit_runs function for determining which code runs to execute."""
|
||||
# Test hospital claim type
|
||||
result = code_funcs.get_implicit_runs({"AARETE_DERIVED_CLAIM_TYPE_CD": "H"})
|
||||
self.assertTrue(result["REVENUE_CD"])
|
||||
|
||||
# Test medical claim type with bill type
|
||||
result = code_funcs.get_implicit_runs(
|
||||
{"AARETE_DERIVED_CLAIM_TYPE_CD": "M", "BILL_TYPE_CD_DESC": "Outpatient"}
|
||||
)
|
||||
self.assertTrue(result["REVENUE_CD"])
|
||||
self.assertTrue(result["PROCEDURE_CD"])
|
||||
|
||||
# Test inpatient bill type
|
||||
result = code_funcs.get_implicit_runs(
|
||||
{
|
||||
"AARETE_DERIVED_CLAIM_TYPE_CD": "M",
|
||||
"BILL_TYPE_CD_DESC": "Inpatient Hospital",
|
||||
}
|
||||
)
|
||||
self.assertTrue(result["REVENUE_CD"])
|
||||
self.assertFalse(result["PROCEDURE_CD"])
|
||||
|
||||
@patch("src.codes.code_funcs.clean_service")
|
||||
@patch("src.codes.code_funcs.fill_bill_type")
|
||||
@patch("src.codes.code_funcs.get_implicit_runs")
|
||||
@patch("src.codes.code_funcs.code_explicit")
|
||||
@patch("src.codes.code_funcs.code_category")
|
||||
@patch("src.codes.code_funcs.code_implicit_special")
|
||||
@patch("src.codes.code_funcs.code_implicit_rag")
|
||||
@patch("src.codes.code_funcs.code_last_check")
|
||||
def test_extract_codes_from_service(
|
||||
self,
|
||||
mock_last_check,
|
||||
mock_implicit_rag,
|
||||
mock_implicit_special,
|
||||
mock_category,
|
||||
mock_explicit,
|
||||
mock_implicit_runs,
|
||||
mock_fill_bill_type,
|
||||
mock_clean_service,
|
||||
):
|
||||
"""Test the extract_codes_from_service function end-to-end."""
|
||||
# Setup mocks
|
||||
mock_clean_service.return_value = "CLEAN SERVICE"
|
||||
mock_fill_bill_type.return_value = {
|
||||
"SERVICE_TERM": "TEST SERVICE",
|
||||
"BILL_TYPE_CD_DESC": "Outpatient",
|
||||
}
|
||||
mock_implicit_runs.return_value = {"PROCEDURE_CD": True, "REVENUE_CD": True}
|
||||
|
||||
# Test explicit code match - extract_codes_from_service normalizes to JSON list format
|
||||
mock_explicit.return_value = {"PROCEDURE_CD": "12345"}
|
||||
result = code_funcs.extract_codes_from_service(
|
||||
{"SERVICE_TERM": "TEST SERVICE"}, self.constants
|
||||
)
|
||||
self.assertEqual(result["PROCEDURE_CD"], '["12345"]')
|
||||
self.assertEqual(result["CODE_METHODOLOGY"], "Explicit")
|
||||
|
||||
# Test unlisted service
|
||||
mock_clean_service.return_value = "UNLISTED"
|
||||
result = code_funcs.extract_codes_from_service(
|
||||
{"SERVICE_TERM": "UNLISTED SERVICE"}, self.constants
|
||||
)
|
||||
self.assertEqual(result["PROCEDURE_CD_DESC"], "UNLISTED")
|
||||
|
||||
# Test category match
|
||||
mock_clean_service.return_value = "CLEAN SERVICE"
|
||||
mock_explicit.return_value = {"PROCEDURE_CD": ["Category: J"]}
|
||||
mock_category.return_value = {
|
||||
"PROCEDURE_CD": ["J0001"],
|
||||
"PROCEDURE_CD_DESC": ["Drug A"],
|
||||
}
|
||||
result = code_funcs.extract_codes_from_service(
|
||||
{"SERVICE_TERM": "J CODES"}, self.constants
|
||||
)
|
||||
self.assertEqual(result["CODE_METHODOLOGY"], "Implicit - Letter Category")
|
||||
|
||||
# Test special case match
|
||||
mock_explicit.return_value = {}
|
||||
mock_implicit_special.return_value = {
|
||||
"PROCEDURE_CD": "J0000-J9999",
|
||||
"PROCEDURE_CD_DESC": "Drugs",
|
||||
}
|
||||
result = code_funcs.extract_codes_from_service(
|
||||
{"SERVICE_TERM": "DRUG SERVICE"}, self.constants
|
||||
)
|
||||
self.assertEqual(result["CODE_METHODOLOGY"], "Implicit - Special Case")
|
||||
|
||||
# Test RAG match
|
||||
mock_implicit_special.return_value = {}
|
||||
mock_implicit_rag.return_value = {
|
||||
"PROCEDURE_CD": "12345",
|
||||
"CODE_METHODOLOGY": "Implicit - Level 1",
|
||||
}
|
||||
result = code_funcs.extract_codes_from_service(
|
||||
{"SERVICE_TERM": "TEST SERVICE"}, self.constants
|
||||
)
|
||||
self.assertEqual(result["CODE_METHODOLOGY"], "Implicit - Level 1")
|
||||
|
||||
# Test no match - specific
|
||||
mock_implicit_rag.return_value = {}
|
||||
mock_last_check.return_value = "Specific"
|
||||
result = code_funcs.extract_codes_from_service(
|
||||
{"SERVICE_TERM": "TEST SERVICE"}, self.constants
|
||||
)
|
||||
self.assertEqual(result["CODE_METHODOLOGY"], "Specific - No Match")
|
||||
|
||||
# Test no match - generic
|
||||
mock_last_check.return_value = "Generic"
|
||||
result = code_funcs.extract_codes_from_service(
|
||||
{"SERVICE_TERM": "TEST SERVICE"}, self.constants
|
||||
)
|
||||
self.assertEqual(result["CODE_METHODOLOGY"], "Generic - No Match")
|
||||
|
||||
def test_fill_claim_type(self):
|
||||
"""Test the fill_claim_type function for determining claim types."""
|
||||
# Test with existing claim types
|
||||
answer_dicts = [
|
||||
{"AARETE_DERIVED_CLAIM_TYPE_CD": "H"},
|
||||
{"AARETE_DERIVED_CLAIM_TYPE_CD": "H"},
|
||||
{"AARETE_DERIVED_CLAIM_TYPE_CD": "M"},
|
||||
{},
|
||||
]
|
||||
result = code_funcs.fill_claim_type(answer_dicts)
|
||||
self.assertEqual(result[3]["AARETE_DERIVED_CLAIM_TYPE_CD"], "H")
|
||||
|
||||
# Test with all empty claim types
|
||||
answer_dicts = [{}, {}]
|
||||
result = code_funcs.fill_claim_type(answer_dicts)
|
||||
self.assertEqual(result, answer_dicts)
|
||||
|
||||
@patch("src.codes.code_funcs.fill_claim_type")
|
||||
@patch("src.codes.code_funcs.extract_codes_from_service")
|
||||
def test_code_breakout(self, mock_extract_codes, mock_fill_claim_type):
|
||||
"""Test the code_breakout function for processing DataFrame records."""
|
||||
# Create test data
|
||||
test_df = pd.DataFrame(
|
||||
[{"SERVICE_TERM": "SERVICE A"}, {"SERVICE_TERM": "SERVICE B"}]
|
||||
)
|
||||
|
||||
# Setup mocks
|
||||
mock_fill_claim_type.return_value = [
|
||||
{"SERVICE_TERM": "SERVICE A"},
|
||||
{"SERVICE_TERM": "SERVICE B"},
|
||||
]
|
||||
|
||||
mock_extract_codes.side_effect = [
|
||||
{"SERVICE_TERM": "SERVICE A", "PROCEDURE_CD": "12345"},
|
||||
{"SERVICE_TERM": "SERVICE B", "PROCEDURE_CD": "67890"},
|
||||
]
|
||||
|
||||
# Test function
|
||||
result_df = code_funcs.code_breakout(test_df, self.constants)
|
||||
|
||||
# Verify results - code_breakout normalizes to JSON list format
|
||||
self.assertIsInstance(result_df, pd.DataFrame)
|
||||
self.assertEqual(len(result_df), 2)
|
||||
self.assertEqual(result_df.iloc[0]["PROCEDURE_CD"], '["12345"]')
|
||||
self.assertEqual(result_df.iloc[1]["PROCEDURE_CD"], '["67890"]')
|
||||
|
||||
# Verify mock calls
|
||||
mock_fill_claim_type.assert_called_once()
|
||||
self.assertEqual(mock_extract_codes.call_count, 2)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user