2025-08-27 16:37:48 +00:00
|
|
|
import logging
|
2025-08-05 20:46:19 +00:00
|
|
|
|
2025-08-29 14:38:15 +00:00
|
|
|
import pandas as pd
|
2025-08-05 20:46:19 +00:00
|
|
|
import src.codes.code_funcs as code_funcs
|
2025-12-10 20:01:52 +00:00
|
|
|
from src.investment import preprocessing_funcs, aarete_derived, dynamic_funcs, one_to_n_funcs, one_to_one_funcs, postprocess, postprocessing_funcs, preprocess, hybrid_smart_chunking_funcs, row_funcs, tin_npi_funcs
|
|
|
|
|
from src.utils import io_utils, logging_utils, string_utils
|
2025-08-05 20:46:19 +00:00
|
|
|
from constants.constants import Constants
|
2025-04-25 20:28:52 +00:00
|
|
|
from src import config
|
2025-12-17 19:15:49 +00:00
|
|
|
from src.prompts.fieldset import FieldSet, Field
|
2025-04-25 20:28:52 +00:00
|
|
|
from src.utils.string_utils import datetime_str
|
|
|
|
|
|
2025-04-08 18:29:39 +00:00
|
|
|
|
2025-08-05 20:46:19 +00:00
|
|
|
def process_file(file_object, constants: Constants, run_timestamp):
|
2024-10-28 23:39:36 +00:00
|
|
|
filename, contract_text = file_object
|
2025-08-29 14:38:15 +00:00
|
|
|
|
2025-09-24 21:31:38 +00:00
|
|
|
# Set per-file logging context:
|
|
|
|
|
# With this, all logging calls in this thread will now route to logs/{filename}.log
|
|
|
|
|
# This includes logging from all called functions (preprocess, one_to_n_funcs, etc.)
|
2025-08-29 14:38:15 +00:00
|
|
|
logging_utils.set_current_file(filename)
|
2025-08-27 16:37:48 +00:00
|
|
|
logging.info(f"{datetime_str()} Processing {filename}...")
|
2024-11-13 17:08:19 +00:00
|
|
|
|
2025-10-23 19:57:11 +00:00
|
|
|
# Set default values
|
|
|
|
|
dynamic_one_to_one_fields = FieldSet()
|
|
|
|
|
process_one_to_n = config.FIELDS in ['all', 'one_to_n']
|
|
|
|
|
process_one_to_one = config.FIELDS in ['all', 'one_to_one']
|
|
|
|
|
# Initialize default fallback for final results
|
|
|
|
|
final_results = pd.DataFrame([{"FILE_NAME": filename}])
|
|
|
|
|
|
2025-01-27 19:39:23 +00:00
|
|
|
################## PREPROCESS ##################
|
2025-01-10 17:24:44 +00:00
|
|
|
contract_text = preprocess.clean_text(contract_text)
|
2025-01-27 19:39:23 +00:00
|
|
|
text_dict, top_sheet_dict = preprocess.split_text(contract_text)
|
2025-08-27 17:42:35 +00:00
|
|
|
text_dict, header, footer = preprocess.find_headers_and_footers(text_dict)
|
2025-10-13 18:54:10 +00:00
|
|
|
|
|
|
|
|
# ONE TO N PROCESSING
|
2025-10-27 16:22:43 +00:00
|
|
|
one_to_n_results = pd.DataFrame() # Initialize empty DataFrame for one_to_n_results
|
2025-10-13 18:54:10 +00:00
|
|
|
if process_one_to_n:
|
2025-12-10 20:01:52 +00:00
|
|
|
exhibit_chunk_mapping, all_exhibit_headers = preprocess.one_to_n_exhibit_chunking(
|
2025-10-13 18:54:10 +00:00
|
|
|
text_dict, constants.EXHIBIT_HEADER_MARKERS, filename
|
|
|
|
|
)
|
|
|
|
|
logging.info(f"{datetime_str()} Preprocessing Complete - {filename}")
|
|
|
|
|
|
|
|
|
|
one_to_n_results = pd.DataFrame([{"FILE_NAME": filename}]) # Initialize here
|
|
|
|
|
|
|
|
|
|
if string_utils.contains_reimbursement(contract_text):
|
2025-12-10 20:01:52 +00:00
|
|
|
one_to_n_results, dynamic_one_to_one_fields, first_reimbursement_page = run_one_to_n_prompts(
|
|
|
|
|
text_dict, exhibit_chunk_mapping, all_exhibit_headers, constants, filename
|
2025-10-13 18:54:10 +00:00
|
|
|
)
|
2025-10-23 19:57:11 +00:00
|
|
|
if not one_to_n_results.empty:
|
|
|
|
|
one_to_n_results["FILE_NAME"] = filename
|
|
|
|
|
one_to_n_results = postprocessing_funcs.generate_reimb_ids(one_to_n_results)
|
2025-10-13 18:54:10 +00:00
|
|
|
logging.info(f"{datetime_str()} One to N Complete - {filename}")
|
|
|
|
|
else:
|
2025-12-10 20:01:52 +00:00
|
|
|
first_reimbursement_page = '1'
|
2025-10-13 18:54:10 +00:00
|
|
|
logging.info(f"{datetime_str()} No Reimbursement Found, Skipping - {filename}")
|
|
|
|
|
|
|
|
|
|
final_results = one_to_n_results # Set as final results
|
2025-01-27 19:39:23 +00:00
|
|
|
else:
|
2025-12-10 20:01:52 +00:00
|
|
|
first_reimbursement_page = '1'
|
2025-10-13 18:54:10 +00:00
|
|
|
logging.info(f"{datetime_str()} Fields not configured for One to N, Skipping - {filename}")
|
|
|
|
|
|
|
|
|
|
# ONE TO ONE PROCESSING
|
2025-12-12 18:36:57 +00:00
|
|
|
if process_one_to_one:
|
2025-10-13 18:54:10 +00:00
|
|
|
one_to_one_results = run_one_to_one_prompts(
|
|
|
|
|
filename,
|
|
|
|
|
contract_text,
|
|
|
|
|
text_dict,
|
|
|
|
|
top_sheet_dict,
|
|
|
|
|
dynamic_one_to_one_fields,
|
2025-12-10 20:01:52 +00:00
|
|
|
first_reimbursement_page,
|
2025-10-13 18:54:10 +00:00
|
|
|
constants,
|
|
|
|
|
)
|
|
|
|
|
one_to_one_results["FILE_NAME"] = filename
|
|
|
|
|
logging.info(f"{datetime_str()} One to One Complete - {filename}")
|
|
|
|
|
|
|
|
|
|
# Decide how to handle one_to_one results
|
2025-10-23 19:57:11 +00:00
|
|
|
if not one_to_n_results.empty:
|
2025-10-13 18:54:10 +00:00
|
|
|
# BOTH processed - merge into one_to_n
|
|
|
|
|
final_results = row_funcs.merge_one_to_one_into_one_to_n(
|
|
|
|
|
one_to_n_results, one_to_one_results, constants
|
|
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
# ONLY one_to_one processed - convert dict to DataFrame
|
|
|
|
|
final_results = pd.DataFrame([one_to_one_results])
|
|
|
|
|
else:
|
|
|
|
|
logging.info(f"{datetime_str()} Fields not configured for One to One, Skipping - {filename}")
|
2025-08-01 22:36:01 +00:00
|
|
|
|
2025-10-13 18:54:10 +00:00
|
|
|
# APPLY CODES IF ONE_TO_N WAS PROCESSED
|
2025-10-23 19:57:11 +00:00
|
|
|
if not one_to_n_results.empty:
|
2025-10-13 18:54:10 +00:00
|
|
|
results_with_code = code_funcs.code_breakout(final_results, constants)
|
|
|
|
|
final_results = code_funcs.grouper_breakout(results_with_code)
|
|
|
|
|
logging.info(f"{datetime_str()} Codes Complete - {filename}")
|
2025-11-19 18:42:04 +00:00
|
|
|
|
2025-08-26 15:43:53 +00:00
|
|
|
|
2025-10-13 18:54:10 +00:00
|
|
|
# POSTPROCESS
|
2025-08-05 20:46:19 +00:00
|
|
|
final_df = postprocess.postprocess(final_results, constants)
|
2025-08-27 16:37:48 +00:00
|
|
|
logging.info(f"{datetime_str()} Postprocessing Complete - {filename}")
|
2025-11-19 18:42:04 +00:00
|
|
|
|
2025-01-27 19:39:23 +00:00
|
|
|
################## WRITE INDIVIDUAL ##################
|
|
|
|
|
if config.WRITE_TO_S3:
|
|
|
|
|
io_utils.write_s3(final_df, filename, run_timestamp, "individual")
|
|
|
|
|
else:
|
|
|
|
|
io_utils.write_local(final_df, filename, "", "individual")
|
|
|
|
|
|
2025-08-27 16:37:48 +00:00
|
|
|
logging.info(f"{datetime_str()} Writing Complete - {filename}")
|
2025-12-10 20:01:52 +00:00
|
|
|
|
2025-01-27 19:39:23 +00:00
|
|
|
return final_df
|
|
|
|
|
|
|
|
|
|
|
2025-08-05 20:46:19 +00:00
|
|
|
def run_one_to_one_prompts(
|
2025-11-21 22:38:49 +00:00
|
|
|
filename: str,
|
|
|
|
|
contract_text: str,
|
|
|
|
|
text_dict: dict[str, str],
|
2025-12-10 20:01:52 +00:00
|
|
|
top_sheet_dict,
|
2025-11-21 22:38:49 +00:00
|
|
|
dynamic_one_to_one_fields: FieldSet,
|
2025-12-10 20:01:52 +00:00
|
|
|
first_reimbursement_page: str,
|
2025-11-21 22:38:49 +00:00
|
|
|
constants: Constants,
|
2025-12-10 20:01:52 +00:00
|
|
|
):
|
2024-10-28 23:39:36 +00:00
|
|
|
|
2025-01-27 19:39:23 +00:00
|
|
|
################## INITIALIZE FIELDS ##################
|
2025-08-05 20:46:19 +00:00
|
|
|
one_to_one_fields = FieldSet(
|
|
|
|
|
relationship="one_to_one", file_path=config.FIELD_JSON_PATH
|
|
|
|
|
).combine(dynamic_one_to_one_fields)
|
2024-12-02 19:50:41 +00:00
|
|
|
|
2025-12-17 19:15:49 +00:00
|
|
|
|
2025-06-26 19:35:32 +00:00
|
|
|
################## RUN PROVIDER INFO ##################
|
2025-08-05 20:46:19 +00:00
|
|
|
one_to_one_results, one_to_one_fields = tin_npi_funcs.run_provider_info_fields(
|
|
|
|
|
contract_text, one_to_one_fields, text_dict, filename
|
|
|
|
|
)
|
2025-11-05 15:20:33 +00:00
|
|
|
|
|
|
|
|
################## RUN HYBRID SMART CHUNKED PROMPTS ##################
|
|
|
|
|
# RAG function loads retrieval questions internally and matches with investment_prompts.json
|
|
|
|
|
hybrid_smart_chunked_answers_dict = hybrid_smart_chunking_funcs.run_hybrid_smart_chunked_fields(
|
|
|
|
|
one_to_one_fields, constants, contract_text, filename, text_dict
|
2024-12-06 18:19:58 +00:00
|
|
|
)
|
2025-12-10 20:01:52 +00:00
|
|
|
|
2025-11-05 15:20:33 +00:00
|
|
|
|
2024-12-06 18:19:58 +00:00
|
|
|
################## RUN FULL CONTEXT PROMPTS ##################
|
2025-01-27 19:39:23 +00:00
|
|
|
full_context_answers_dict = one_to_one_funcs.run_full_context_fields(
|
2025-12-10 20:01:52 +00:00
|
|
|
one_to_one_fields, contract_text, text_dict, first_reimbursement_page, constants, filename
|
2024-12-06 18:19:58 +00:00
|
|
|
)
|
2025-01-31 22:36:11 +00:00
|
|
|
################## COMBINE ANSWERS ################
|
2025-11-05 15:20:33 +00:00
|
|
|
# Prefer HSC answers over full_context when HSC value is not N/A
|
|
|
|
|
for key, value in full_context_answers_dict.items():
|
|
|
|
|
one_to_one_results[key] = value
|
|
|
|
|
|
|
|
|
|
for key, value in hybrid_smart_chunked_answers_dict.items():
|
|
|
|
|
if not string_utils.is_empty(value):
|
|
|
|
|
one_to_one_results[key] = value
|
|
|
|
|
elif key not in one_to_one_results:
|
|
|
|
|
# Add HSC's N/A if field doesn't exist yet
|
|
|
|
|
one_to_one_results[key] = value
|
2025-01-31 22:36:11 +00:00
|
|
|
|
2025-10-30 15:28:39 +00:00
|
|
|
################## ADD AD FIELDS ################
|
|
|
|
|
one_to_one_results = one_to_one_funcs.get_aarete_derived_dates(one_to_one_results, text_dict, filename)
|
2025-11-05 15:20:33 +00:00
|
|
|
|
2025-11-19 18:42:04 +00:00
|
|
|
################## ADD SIGNATURE COUNT ##################
|
2025-11-21 22:38:49 +00:00
|
|
|
one_to_one_results = one_to_one_funcs.add_signature_count(one_to_one_results, contract_text)
|
2025-11-19 18:42:04 +00:00
|
|
|
|
2025-08-05 20:46:19 +00:00
|
|
|
################## Crosswalk Fields ##################
|
2025-09-24 15:56:27 +00:00
|
|
|
one_to_one_results = aarete_derived.get_crosswalk_fields(
|
|
|
|
|
[one_to_one_results], constants
|
|
|
|
|
)
|
2024-11-13 17:08:19 +00:00
|
|
|
|
2025-04-08 18:29:39 +00:00
|
|
|
################## Fill NA Mapping ##################
|
|
|
|
|
one_to_one_results = aarete_derived.fill_na_mapping(one_to_one_results)
|
|
|
|
|
|
|
|
|
|
return one_to_one_results[0]
|
2024-10-28 23:39:36 +00:00
|
|
|
|
|
|
|
|
|
2025-12-10 20:01:52 +00:00
|
|
|
def run_one_to_n_prompts(text_dict: dict[str, str],
|
|
|
|
|
exhibit_chunk_mapping: dict[str, list[str]],
|
|
|
|
|
all_exhibit_headers: dict[str, str],
|
|
|
|
|
constants: Constants,
|
|
|
|
|
filename: str):
|
2025-08-05 20:46:19 +00:00
|
|
|
|
2025-01-31 22:36:11 +00:00
|
|
|
one_to_n_results = []
|
2025-12-10 20:01:52 +00:00
|
|
|
first_reimbursement_page = '1'
|
2025-12-17 19:15:49 +00:00
|
|
|
previous_exhibit = None
|
2025-09-24 15:56:27 +00:00
|
|
|
|
2025-12-10 20:01:52 +00:00
|
|
|
#################################### PROCESS EACH EXHIBIT SEPARATELY ####################################
|
|
|
|
|
for exhibit_page, exhibit_page_nums in exhibit_chunk_mapping.items():
|
2025-09-24 15:56:27 +00:00
|
|
|
|
2025-12-10 20:01:52 +00:00
|
|
|
exhibit_text_original = "\n".join([text_dict[page_num] for page_num in exhibit_page_nums])
|
|
|
|
|
exhibit_header = all_exhibit_headers.get(exhibit_page, "")
|
2025-09-24 15:56:27 +00:00
|
|
|
|
|
|
|
|
############################### Get Exhibit Level ###############################
|
2025-12-10 20:01:52 +00:00
|
|
|
exhibit_level_answers, dynamic_reimbursement_fields = (
|
2025-09-24 15:56:27 +00:00
|
|
|
one_to_n_funcs.exhibit_level(
|
2025-12-10 20:01:52 +00:00
|
|
|
exhibit_text_original,
|
2025-09-24 15:56:27 +00:00
|
|
|
exhibit_header,
|
|
|
|
|
exhibit_page,
|
|
|
|
|
constants,
|
|
|
|
|
filename,
|
2025-08-05 20:46:19 +00:00
|
|
|
)
|
2025-12-10 20:01:52 +00:00
|
|
|
) # dict, FieldSet
|
2025-10-22 15:21:43 +00:00
|
|
|
logging.debug(f"Exhibit Level Answers for {filename}, Page {exhibit_page}: {exhibit_level_answers}")
|
2025-12-17 19:15:49 +00:00
|
|
|
|
|
|
|
|
# Store original values before inheritance (for tracking in previous_exhibit)
|
|
|
|
|
original_exhibit_page_nums = exhibit_page_nums.copy()
|
|
|
|
|
original_exhibit_text = exhibit_text_original
|
|
|
|
|
original_dynamic_reimbursement_fields = dynamic_reimbursement_fields
|
|
|
|
|
|
|
|
|
|
############################### Check and Combine Exhibit Inheritance ###############################
|
|
|
|
|
# IF dynamic_reimbursement_fields is empty FieldSet, and previous exhibit's dynamic_reimbursement_fields is NOT empty,
|
|
|
|
|
# and the previous exhibit has NO reimbursement rows, then replace dynamic_reimbursement_fields with the previous
|
|
|
|
|
# exhibit's dynamic_reimbursement_field values AND set exhibit_page_nums = exhibit_page_nums for exhibit N-1 + exhibit_page_nums for exhibit N
|
|
|
|
|
should_inherit, exhibit_page_nums, dynamic_reimbursement_fields = (
|
|
|
|
|
one_to_n_funcs.check_and_combine_exhibit_inheritance(
|
|
|
|
|
previous_exhibit,
|
|
|
|
|
exhibit_page_nums,
|
|
|
|
|
exhibit_text_original,
|
|
|
|
|
dynamic_reimbursement_fields,
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
|
2025-12-10 20:01:52 +00:00
|
|
|
all_exhibit_reimbursements, all_exhibit_special_case = [], []
|
|
|
|
|
#################################### PAGE-BY-PAGE REIMBURSEMENT PRIMARY ####################################
|
|
|
|
|
for page_num in exhibit_page_nums:
|
|
|
|
|
reimbursement_level_answers, special_case_answers, first_reimbursement_page = process_page_one_to_n(
|
|
|
|
|
text_dict,
|
|
|
|
|
exhibit_page_nums,
|
2025-09-24 15:56:27 +00:00
|
|
|
exhibit_page,
|
2025-12-10 20:01:52 +00:00
|
|
|
page_num,
|
|
|
|
|
first_reimbursement_page,
|
|
|
|
|
dynamic_reimbursement_fields,
|
2025-09-24 15:56:27 +00:00
|
|
|
filename,
|
2025-12-10 20:01:52 +00:00
|
|
|
constants
|
2025-09-24 15:56:27 +00:00
|
|
|
)
|
2025-12-10 20:01:52 +00:00
|
|
|
all_exhibit_reimbursements += reimbursement_level_answers
|
|
|
|
|
all_exhibit_special_case += special_case_answers
|
2025-05-13 20:22:43 +00:00
|
|
|
|
2025-09-24 15:56:27 +00:00
|
|
|
################################ Combine Answers ###############################
|
|
|
|
|
all_exhibit_rows = row_funcs.combine_one_to_n(
|
2025-12-10 20:01:52 +00:00
|
|
|
exhibit_text_original,
|
|
|
|
|
all_exhibit_reimbursements,
|
|
|
|
|
all_exhibit_special_case,
|
2025-09-24 15:56:27 +00:00
|
|
|
exhibit_level_answers,
|
2025-12-10 20:01:52 +00:00
|
|
|
filename
|
2025-09-24 15:56:27 +00:00
|
|
|
) # returns list of dicts
|
|
|
|
|
|
2025-10-07 15:06:10 +00:00
|
|
|
################################ Mapping and Cleaning ###############################
|
2025-12-10 20:01:52 +00:00
|
|
|
all_exhibit_rows = one_to_n_funcs.one_to_n_cleaning(all_exhibit_rows, exhibit_text_original, constants, filename)
|
2025-04-08 18:29:39 +00:00
|
|
|
|
2025-09-24 15:56:27 +00:00
|
|
|
################################ Add to Total ###############################
|
|
|
|
|
one_to_n_results += all_exhibit_rows
|
2025-05-07 23:03:16 +00:00
|
|
|
|
2025-12-17 19:15:49 +00:00
|
|
|
################################ Store Current Exhibit for Next Iteration ###############################
|
|
|
|
|
# Store original (non-combined) values for next exhibit to potentially inherit from
|
|
|
|
|
# Determine if current exhibit had reimbursements
|
|
|
|
|
had_reimbursements = len(all_exhibit_reimbursements) > 0 or len(all_exhibit_special_case) > 0
|
|
|
|
|
|
|
|
|
|
previous_exhibit = {
|
|
|
|
|
'exhibit_page': exhibit_page,
|
|
|
|
|
'exhibit_page_nums': original_exhibit_page_nums,
|
|
|
|
|
'exhibit_text_original': original_exhibit_text,
|
|
|
|
|
'dynamic_reimbursement_fields': original_dynamic_reimbursement_fields,
|
|
|
|
|
'exhibit_level_answers': exhibit_level_answers.copy(),
|
|
|
|
|
'had_reimbursements': had_reimbursements
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-08 18:29:39 +00:00
|
|
|
################## Add N/A Dynamic or Exhibit to One-to-One ##################
|
2025-08-05 20:46:19 +00:00
|
|
|
dynamic_one_to_one_fields = dynamic_funcs.get_dynamic_one_to_one_fields(
|
2025-12-10 20:01:52 +00:00
|
|
|
one_to_n_results, constants
|
2025-08-05 20:46:19 +00:00
|
|
|
)
|
2025-12-12 18:36:57 +00:00
|
|
|
|
2025-01-31 22:36:11 +00:00
|
|
|
################## CONVERT TO DF ##################
|
2025-01-27 19:39:23 +00:00
|
|
|
one_to_n_df = pd.DataFrame(one_to_n_results)
|
2025-02-21 14:44:36 +00:00
|
|
|
|
2025-12-10 20:01:52 +00:00
|
|
|
return one_to_n_df, dynamic_one_to_one_fields, first_reimbursement_page
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def process_page_one_to_n(text_dict: dict[str, str], exhibit_page_nums: list[str], exhibit_page: str, page_num: str, first_reimbursement_page: str, dynamic_reimbursement_fields: FieldSet, filename: str, constants: Constants):
|
|
|
|
|
|
|
|
|
|
page_text = text_dict[page_num]
|
|
|
|
|
exhibit_text_simplified = preprocessing_funcs.simplify_exhibit(text_dict, exhibit_page_nums, page_num)
|
2025-12-12 18:36:57 +00:00
|
|
|
|
2025-12-10 20:01:52 +00:00
|
|
|
############################### Reimbursement Primary ###############################
|
|
|
|
|
reimbursement_level_answers = one_to_n_funcs.reimbursement_level(
|
|
|
|
|
page_text,
|
|
|
|
|
constants,
|
|
|
|
|
filename
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if not reimbursement_level_answers:
|
|
|
|
|
return [], [], first_reimbursement_page
|
|
|
|
|
|
|
|
|
|
# Track the first page with reimbursements
|
|
|
|
|
if first_reimbursement_page == '1':
|
|
|
|
|
first_reimbursement_page = exhibit_page
|
|
|
|
|
|
|
|
|
|
logging.debug(f"Reimbursement Primary Answers for {filename}, Page {page_num}: {reimbursement_level_answers}")
|
|
|
|
|
|
|
|
|
|
################################ Carveouts and Special Case ################################
|
|
|
|
|
reimbursement_level_answers, special_case_answers = one_to_n_funcs.carveout_and_special_case(
|
|
|
|
|
reimbursement_level_answers, constants, filename
|
|
|
|
|
) # Breaks the reimbursement answers into reimbursments (regular lines) and special cases (distributed across exhibit)
|
|
|
|
|
|
|
|
|
|
############################### Dynamic Assignment ###############################
|
|
|
|
|
reimbursement_level_answers = dynamic_funcs.dynamic_assignment(reimbursement_level_answers, dynamic_reimbursement_fields, exhibit_text_simplified, page_num, constants, filename)
|
|
|
|
|
|
|
|
|
|
############################### Lesser of Distribution ###############################
|
|
|
|
|
reimbursement_level_answers = one_to_n_funcs.lesser_of_distribution(reimbursement_level_answers, exhibit_text_simplified, page_num, constants, filename)
|
|
|
|
|
|
|
|
|
|
################################ Get Breakouts ###############################
|
|
|
|
|
reimbursement_level_answers, special_case_answers = one_to_n_funcs.breakout(
|
|
|
|
|
reimbursement_level_answers,
|
|
|
|
|
special_case_answers,
|
|
|
|
|
filename,
|
|
|
|
|
constants,
|
|
|
|
|
) # list[dict[str, str]], list[dict[str, str]]
|
|
|
|
|
|
2025-12-10 22:24:44 +00:00
|
|
|
return reimbursement_level_answers, special_case_answers, first_reimbursement_page
|