2024-10-28 23:39:36 +00:00
|
|
|
import pandas as pd
|
2025-08-05 20:46:19 +00:00
|
|
|
|
|
|
|
|
import src.codes.code_funcs as code_funcs
|
2025-04-25 20:28:52 +00:00
|
|
|
import src.investment.aarete_derived as aarete_derived
|
|
|
|
|
import src.investment.dynamic_funcs as dynamic_funcs
|
2025-01-08 21:59:47 +00:00
|
|
|
import src.investment.one_to_n_funcs as one_to_n_funcs
|
2025-01-27 19:39:23 +00:00
|
|
|
import src.investment.one_to_one_funcs as one_to_one_funcs
|
2025-01-08 21:59:47 +00:00
|
|
|
import src.investment.postprocess as postprocess
|
2025-08-05 20:46:19 +00:00
|
|
|
import src.investment.postprocessing_funcs as postprocessing_funcs
|
2025-01-08 21:59:47 +00:00
|
|
|
import src.investment.preprocess as preprocess
|
2025-06-09 21:49:45 +00:00
|
|
|
import src.investment.row_funcs as row_funcs
|
2025-08-05 20:46:19 +00:00
|
|
|
import src.investment.tin_npi_funcs as tin_npi_funcs
|
2025-01-27 19:39:23 +00:00
|
|
|
import src.utils.io_utils as io_utils
|
2025-04-25 20:28:52 +00:00
|
|
|
import src.utils.string_utils as 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-01-27 21:27:15 +00:00
|
|
|
from src.config import FIELD_JSON_PATH
|
2025-08-05 20:46:19 +00:00
|
|
|
from src.investment.one_to_n_funcs import combine_one_to_n_answers, reimbursement_level
|
2025-08-01 22:36:01 +00:00
|
|
|
from src.prompts.investment_prompts import FieldSet
|
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-02-20 20:19:53 +00:00
|
|
|
print(f"{datetime_str()} Processing {filename}...")
|
2024-11-13 17:08:19 +00:00
|
|
|
|
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-05 20:46:19 +00:00
|
|
|
text_dict = preprocess.clean_tables(
|
|
|
|
|
text_dict, constants.EXHIBIT_HEADER_MARKERS, filename
|
|
|
|
|
)
|
|
|
|
|
exhibit_dict, all_exhibit_headers = preprocess.one_to_n_exhibit_chunking(
|
|
|
|
|
text_dict, constants.EXHIBIT_HEADER_MARKERS, filename
|
|
|
|
|
)
|
2025-02-20 20:19:53 +00:00
|
|
|
print(f"{datetime_str()} Preprocessing Complete - {filename}")
|
2025-01-27 19:39:23 +00:00
|
|
|
|
|
|
|
|
################## ONE TO N ##################
|
|
|
|
|
if string_utils.contains_reimbursement(contract_text):
|
2025-08-05 20:46:19 +00:00
|
|
|
one_to_n_results, dynamic_one_to_one_fields = run_one_to_n_prompts(
|
|
|
|
|
filename, exhibit_dict, all_exhibit_headers, constants
|
|
|
|
|
) # Return df
|
|
|
|
|
one_to_n_results["FILE_NAME"] = filename
|
|
|
|
|
one_to_n_results = postprocessing_funcs.generate_reimb_ids(
|
|
|
|
|
one_to_n_results
|
|
|
|
|
) # Add reimb_id
|
2025-02-25 17:11:02 +00:00
|
|
|
print(f"{datetime_str()} One to N Complete - {filename}")
|
2025-01-27 19:39:23 +00:00
|
|
|
else:
|
2025-08-05 20:46:19 +00:00
|
|
|
one_to_n_results = pd.DataFrame([{"FILE_NAME": filename}])
|
2025-04-08 18:29:39 +00:00
|
|
|
dynamic_one_to_one_fields = FieldSet()
|
2025-02-25 17:11:02 +00:00
|
|
|
print(f"{datetime_str()} No One to N Found, Skipping - {filename}")
|
2025-08-05 20:46:19 +00:00
|
|
|
|
2025-02-25 17:11:02 +00:00
|
|
|
################## ONE TO ONE ##################
|
2025-08-05 20:46:19 +00:00
|
|
|
one_to_one_results = run_one_to_one_prompts(
|
|
|
|
|
filename, contract_text, text_dict, top_sheet_dict, dynamic_one_to_one_fields
|
|
|
|
|
) # Return dict
|
|
|
|
|
one_to_one_results["FILE_NAME"] = filename
|
2025-02-25 17:11:02 +00:00
|
|
|
print(f"{datetime_str()} One to One Complete - {filename}")
|
|
|
|
|
|
|
|
|
|
################## MERGE ##################
|
2025-08-05 20:46:19 +00:00
|
|
|
merged_results = row_funcs.merge_one_to_one_into_one_to_n(
|
|
|
|
|
one_to_n_results, one_to_one_results
|
|
|
|
|
)
|
2025-08-01 22:36:01 +00:00
|
|
|
|
|
|
|
|
################## CODES ##################
|
2025-08-05 20:46:19 +00:00
|
|
|
final_results = code_funcs.code_breakout(merged_results, constants)
|
2025-04-08 18:29:39 +00:00
|
|
|
|
2025-01-27 19:39:23 +00:00
|
|
|
################## POSTPROCESS ##################
|
2025-08-05 20:46:19 +00:00
|
|
|
final_df = postprocess.postprocess(final_results, constants)
|
2025-02-20 20:19:53 +00:00
|
|
|
print(f"{datetime_str()} Postprocessing Complete - {filename}")
|
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-02-20 20:19:53 +00:00
|
|
|
print(f"{datetime_str()} Writing Complete - {filename}")
|
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(
|
|
|
|
|
filename, contract_text, text_dict, top_sheet_dict, dynamic_one_to_one_fields
|
|
|
|
|
):
|
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-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
|
|
|
|
|
)
|
|
|
|
|
|
2024-12-06 18:19:58 +00:00
|
|
|
################## RUN SMART CHUNKED PROMPTS ##################
|
2025-01-27 19:39:23 +00:00
|
|
|
smart_chunked_answers_dict = one_to_one_funcs.run_smart_chunked_fields(
|
2025-08-05 20:46:19 +00:00
|
|
|
one_to_one_fields, contract_text, filename, text_dict
|
2024-12-06 18:19:58 +00:00
|
|
|
)
|
2024-10-28 23:39:36 +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(
|
|
|
|
|
one_to_one_fields, contract_text, filename
|
2024-12-06 18:19:58 +00:00
|
|
|
)
|
2024-11-18 21:01:09 +00:00
|
|
|
|
2025-01-31 22:36:11 +00:00
|
|
|
################## COMBINE ANSWERS ################
|
2025-04-18 20:50:25 +00:00
|
|
|
one_to_one_results.update(smart_chunked_answers_dict)
|
|
|
|
|
one_to_one_results.update(full_context_answers_dict)
|
2025-01-31 22:36:11 +00:00
|
|
|
|
2025-08-05 20:46:19 +00:00
|
|
|
################## Crosswalk Fields ##################
|
2025-04-08 18:29:39 +00:00
|
|
|
one_to_one_results = aarete_derived.get_crosswalk_fields([one_to_one_results])
|
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-08-05 20:46:19 +00:00
|
|
|
def run_one_to_n_prompts(filename, exhibit_dict, all_exhibit_headers, constants):
|
|
|
|
|
|
2025-01-27 19:39:23 +00:00
|
|
|
################## RUN PROMPTS ##################
|
2025-01-31 22:36:11 +00:00
|
|
|
one_to_n_results = []
|
2025-08-05 20:46:19 +00:00
|
|
|
seen_pairs = (
|
|
|
|
|
set()
|
|
|
|
|
) # Accumulator for tracking duplicate pairs of (`SERVICE_TERM`, `REIMB_TERM`)
|
2025-06-11 20:54:48 +00:00
|
|
|
|
2025-06-09 21:35:20 +00:00
|
|
|
for exhibit_page in exhibit_dict.keys():
|
2025-08-05 20:46:19 +00:00
|
|
|
|
2025-06-03 20:47:42 +00:00
|
|
|
exhibit_text = exhibit_dict[exhibit_page]
|
2025-05-28 16:57:23 +00:00
|
|
|
|
2025-07-14 22:04:41 +00:00
|
|
|
################# GET EXHIBIT-LEVEL ANSWERS ##################
|
2025-06-10 19:50:15 +00:00
|
|
|
if exhibit_page in all_exhibit_headers.keys():
|
|
|
|
|
exhibit_header = all_exhibit_headers[exhibit_page]
|
2025-08-05 20:46:19 +00:00
|
|
|
elif (
|
|
|
|
|
".".join(exhibit_page.split(".")[:-1]) in all_exhibit_headers.keys()
|
|
|
|
|
): # Take the parent header if it exists
|
2025-06-10 19:50:15 +00:00
|
|
|
exhibit_header = all_exhibit_headers[".".join(exhibit_page.split(".")[:-1])]
|
|
|
|
|
else:
|
|
|
|
|
exhibit_header = "No Header Found"
|
2025-05-13 20:22:43 +00:00
|
|
|
|
2025-08-05 20:46:19 +00:00
|
|
|
exhibit_level_answers = one_to_n_funcs.get_exhibit_level_answers(
|
|
|
|
|
exhibit_text, filename
|
|
|
|
|
)
|
|
|
|
|
exhibit_level_answers["EXHIBIT_PAGE"] = exhibit_page
|
|
|
|
|
exhibit_level_answers["EXHIBIT_TITLE"] = exhibit_header
|
2025-05-13 20:22:43 +00:00
|
|
|
|
2025-07-14 22:04:41 +00:00
|
|
|
# Extract exhibit lesser-of statement for passing to reimbursement_level
|
2025-08-05 20:46:19 +00:00
|
|
|
exhibit_lesser_of = exhibit_level_answers.get(
|
|
|
|
|
"EXHIBIT_LESSER_OF_STATEMENT", "N/A"
|
|
|
|
|
)
|
2025-07-14 22:04:41 +00:00
|
|
|
|
|
|
|
|
################## INITIALIZE FIELDS ##################
|
2025-08-05 20:46:19 +00:00
|
|
|
reimbursement_level_fields = FieldSet(
|
|
|
|
|
relationship="one_to_n",
|
|
|
|
|
field_type="reimbursement_level",
|
|
|
|
|
file_path=FIELD_JSON_PATH,
|
|
|
|
|
)
|
2025-07-14 22:04:41 +00:00
|
|
|
|
2025-05-13 20:22:43 +00:00
|
|
|
################## GET DYNAMIC-PRIMARY ANSWERS ##################
|
2025-08-05 20:46:19 +00:00
|
|
|
dynamic_to_exhibit_level_answers, dynamic_to_reimbursement_level_fields = (
|
|
|
|
|
dynamic_funcs.get_dynamic_primary(
|
|
|
|
|
exhibit_text,
|
|
|
|
|
exhibit_header,
|
|
|
|
|
filename,
|
|
|
|
|
FieldSet(
|
|
|
|
|
relationship="one_to_n",
|
|
|
|
|
field_type="dynamic_primary",
|
|
|
|
|
file_path=config.FIELD_JSON_PATH,
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
)
|
2025-05-13 20:22:43 +00:00
|
|
|
exhibit_level_answers.update(dynamic_to_exhibit_level_answers)
|
2025-08-05 20:46:19 +00:00
|
|
|
reimbursement_level_fields.combine(
|
|
|
|
|
dynamic_to_reimbursement_level_fields, inplace=True
|
|
|
|
|
)
|
2025-06-19 17:20:39 +00:00
|
|
|
|
|
|
|
|
################## GET DYNAMIC-REIMB-INFO ANSWERS ##################
|
2025-08-05 20:46:19 +00:00
|
|
|
(
|
|
|
|
|
reimb_info_to_exhibit_level_answers,
|
|
|
|
|
reimb_info_to_reimbursement_level_fields,
|
|
|
|
|
) = dynamic_funcs.get_dynamic_answers(
|
|
|
|
|
exhibit_text,
|
|
|
|
|
exhibit_header,
|
|
|
|
|
filename,
|
|
|
|
|
FieldSet(
|
|
|
|
|
relationship="one_to_n",
|
|
|
|
|
field_type="dynamic_reimb_info",
|
|
|
|
|
file_path=config.FIELD_JSON_PATH,
|
|
|
|
|
),
|
|
|
|
|
)
|
2025-06-19 17:20:39 +00:00
|
|
|
exhibit_level_answers.update(reimb_info_to_exhibit_level_answers)
|
2025-08-05 20:46:19 +00:00
|
|
|
reimbursement_level_fields.combine(
|
|
|
|
|
reimb_info_to_reimbursement_level_fields, inplace=True
|
|
|
|
|
)
|
2025-06-19 17:20:39 +00:00
|
|
|
|
2025-05-13 20:22:43 +00:00
|
|
|
################## GET DYNAMIC-CODE ANSWERS ##################
|
2025-08-05 20:46:19 +00:00
|
|
|
code_to_exhibit_level_answers, code_to_reimbursement_level_fields = (
|
|
|
|
|
dynamic_funcs.get_dynamic_answers(
|
|
|
|
|
exhibit_text,
|
|
|
|
|
exhibit_header,
|
|
|
|
|
filename,
|
|
|
|
|
FieldSet(
|
|
|
|
|
relationship="one_to_n",
|
|
|
|
|
field_type="dynamic_code",
|
|
|
|
|
file_path=config.FIELD_JSON_PATH,
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
)
|
2025-05-13 20:22:43 +00:00
|
|
|
exhibit_level_answers.update(code_to_exhibit_level_answers)
|
2025-08-05 20:46:19 +00:00
|
|
|
reimbursement_level_fields.combine(
|
|
|
|
|
code_to_reimbursement_level_fields, inplace=True
|
|
|
|
|
)
|
2025-05-13 20:22:43 +00:00
|
|
|
|
|
|
|
|
################## GET REIMBURSEMENT-LEVEL ANSWERS (INCLUDING DYNAMIC) ##################
|
2025-08-05 20:46:19 +00:00
|
|
|
reimbursement_level_answers = reimbursement_level(
|
|
|
|
|
exhibit_text,
|
|
|
|
|
filename,
|
|
|
|
|
reimbursement_level_fields,
|
|
|
|
|
exhibit_page,
|
|
|
|
|
seen_pairs,
|
|
|
|
|
exhibit_lesser_of=exhibit_lesser_of,
|
|
|
|
|
constants=constants,
|
|
|
|
|
) # Return list of dictionaries
|
2025-05-13 20:22:43 +00:00
|
|
|
|
|
|
|
|
################# COMBINE ANSWERS ##################
|
2025-08-05 20:46:19 +00:00
|
|
|
full_answer_dict = combine_one_to_n_answers(
|
|
|
|
|
exhibit_level_answers, reimbursement_level_answers, tin_npi_answers={}
|
|
|
|
|
)
|
2025-05-13 20:22:43 +00:00
|
|
|
one_to_n_results += full_answer_dict
|
2025-08-05 20:46:19 +00:00
|
|
|
|
|
|
|
|
################## Crosswalk Fields ##################
|
2025-03-31 20:07:19 +00:00
|
|
|
one_to_n_results = aarete_derived.get_crosswalk_fields(one_to_n_results)
|
|
|
|
|
|
|
|
|
|
################## Fill NA Mapping ##################
|
|
|
|
|
one_to_n_results = aarete_derived.fill_na_mapping(one_to_n_results)
|
2025-04-08 18:29:39 +00:00
|
|
|
|
2025-05-07 23:03:16 +00:00
|
|
|
################## Update LOB for Duals ##################
|
2025-08-05 20:46:19 +00:00
|
|
|
one_to_n_results = postprocessing_funcs.update_lob_for_duals(one_to_n_results)
|
2025-05-07 23:03:16 +00:00
|
|
|
|
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(
|
|
|
|
|
one_to_n_results
|
|
|
|
|
)
|
2025-04-08 18:29:39 +00:00
|
|
|
|
2025-08-06 19:38:34 +00:00
|
|
|
################### Split REIMB_DATES ##################
|
|
|
|
|
one_to_n_results = one_to_n_funcs.split_reimb_dates(one_to_n_results, filename)
|
|
|
|
|
|
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-08-05 20:46:19 +00:00
|
|
|
return one_to_n_df, dynamic_one_to_one_fields
|