2025-08-05 20:46:19 +00:00
|
|
|
|
2025-01-28 20:51:19 +00:00
|
|
|
import src.config as config
|
2025-02-03 17:29:59 +00:00
|
|
|
import src.utils.string_utils as string_utils
|
2026-01-26 16:52:55 +00:00
|
|
|
from src.constants.constants import Constants
|
2025-08-05 20:46:19 +00:00
|
|
|
from src.crosswalk.crosswalk_builder import CrosswalkBuilder
|
2025-08-11 20:47:52 +00:00
|
|
|
from src.prompts.fieldset import FieldSet
|
2025-08-05 20:46:19 +00:00
|
|
|
from src.utils.crosswalk_utils import apply_crosswalk
|
|
|
|
|
|
2025-01-28 20:51:19 +00:00
|
|
|
|
2025-05-19 20:04:59 +00:00
|
|
|
def fill_na_from_field(results_dict, to_field, from_field, crosswalk_path):
|
|
|
|
|
from_str = results_dict.get(from_field)
|
|
|
|
|
if string_utils.is_empty(from_str):
|
|
|
|
|
return results_dict.get(to_field)
|
|
|
|
|
MAPPING_DICT = CrosswalkBuilder().from_json(crosswalk_path).mapping
|
2025-05-22 19:53:15 +00:00
|
|
|
to_value = apply_crosswalk(from_str, MAPPING_DICT)
|
|
|
|
|
return to_value if to_value else results_dict.get(to_field)
|
2025-02-03 17:29:59 +00:00
|
|
|
|
2025-08-05 20:46:19 +00:00
|
|
|
|
2025-03-31 20:07:19 +00:00
|
|
|
def fill_na_mapping(answer_dicts):
|
|
|
|
|
"""
|
|
|
|
|
Fill in missing values in fields using mappings from other fields.
|
2025-12-22 19:40:31 +00:00
|
|
|
Always checks all crosswalks (PROGRAM and PRODUCT) and merges unique values.
|
2025-08-05 20:46:19 +00:00
|
|
|
|
2025-03-31 20:07:19 +00:00
|
|
|
Args:
|
2025-12-22 19:40:31 +00:00
|
|
|
answer_dicts (list[dict]): List of answer dictionaries
|
2025-08-05 20:46:19 +00:00
|
|
|
|
2025-03-31 20:07:19 +00:00
|
|
|
Returns:
|
2025-12-22 19:40:31 +00:00
|
|
|
list[dict]: List of answer dictionaries with filled/merged values
|
2025-03-31 20:07:19 +00:00
|
|
|
"""
|
|
|
|
|
for answer_dict in answer_dicts:
|
2025-12-22 19:40:31 +00:00
|
|
|
# Collect all AARETE_DERIVED_LOB values from different sources
|
|
|
|
|
all_lob_values = set()
|
2026-01-26 16:52:55 +00:00
|
|
|
|
2025-12-22 19:40:31 +00:00
|
|
|
# Get existing AARETE_DERIVED_LOB values (if any)
|
2026-02-02 23:44:27 -05:00
|
|
|
existing_lob_list = answer_dict.get("AARETE_DERIVED_LOB", "")
|
|
|
|
|
|
|
|
|
|
if not string_utils.is_empty(existing_lob_list):
|
|
|
|
|
for lob_val in existing_lob_list:
|
|
|
|
|
if "|" in existing_lob_list:
|
|
|
|
|
all_lob_values.update(
|
|
|
|
|
v.strip()
|
|
|
|
|
for v in lob_val.split("|")
|
|
|
|
|
if v.strip() and v.strip() != "N/A"
|
|
|
|
|
)
|
|
|
|
|
elif lob_val.strip() and lob_val.strip() != "N/A":
|
|
|
|
|
all_lob_values.add(lob_val)
|
2026-01-26 16:52:55 +00:00
|
|
|
|
2025-12-22 19:40:31 +00:00
|
|
|
# Get AARETE_DERIVED_LOB from AARETE_DERIVED_PROGRAM crosswalk (always check if PROGRAM exists)
|
|
|
|
|
program_lob_values = set()
|
|
|
|
|
if not string_utils.is_empty(answer_dict.get("AARETE_DERIVED_PROGRAM")):
|
2026-02-02 23:44:27 -05:00
|
|
|
program_filled_value_list = fill_na_from_field(
|
2025-08-05 20:46:19 +00:00
|
|
|
answer_dict,
|
|
|
|
|
"AARETE_DERIVED_LOB",
|
|
|
|
|
"AARETE_DERIVED_PROGRAM",
|
2026-01-26 16:52:55 +00:00
|
|
|
"src/constants/mappings/crosswalk_program_lob.json",
|
2025-08-05 20:46:19 +00:00
|
|
|
)
|
2026-01-26 16:52:55 +00:00
|
|
|
if (
|
2026-02-02 23:44:27 -05:00
|
|
|
not string_utils.is_empty(program_filled_value_list) and "N/A" not in program_filled_value_list
|
2026-01-26 16:52:55 +00:00
|
|
|
):
|
2026-02-02 23:44:27 -05:00
|
|
|
for program_lob_val in program_filled_value_list:
|
|
|
|
|
if "|" in program_lob_val:
|
|
|
|
|
program_lob_values = set(
|
|
|
|
|
v.strip()
|
|
|
|
|
for v in program_lob_val.split("|")
|
|
|
|
|
if v.strip() and v.strip() != "N/A"
|
|
|
|
|
)
|
|
|
|
|
elif (
|
|
|
|
|
program_lob_val.strip()
|
|
|
|
|
and program_lob_val.strip() != "N/A"
|
|
|
|
|
):
|
|
|
|
|
program_lob_values = {program_lob_val.strip()}
|
|
|
|
|
all_lob_values.update(program_lob_values)
|
2026-01-26 16:52:55 +00:00
|
|
|
|
2025-12-22 19:40:31 +00:00
|
|
|
# Get AARETE_DERIVED_LOB from PRODUCT crosswalk (always check if PRODUCT exists)
|
|
|
|
|
product_lob_values = set()
|
|
|
|
|
if not string_utils.is_empty(answer_dict.get("PRODUCT")):
|
2026-02-02 23:44:27 -05:00
|
|
|
product_filled_value_list = fill_na_from_field(
|
2025-08-05 20:46:19 +00:00
|
|
|
answer_dict,
|
|
|
|
|
"AARETE_DERIVED_LOB",
|
|
|
|
|
"PRODUCT",
|
2026-01-26 16:52:55 +00:00
|
|
|
"src/constants/mappings/crosswalk_product_lob.json",
|
2025-08-05 20:46:19 +00:00
|
|
|
)
|
2026-01-26 16:52:55 +00:00
|
|
|
if (
|
2026-02-02 23:44:27 -05:00
|
|
|
not string_utils.is_empty(product_filled_value_list)
|
|
|
|
|
and "N/A" not in product_filled_value_list
|
2026-01-26 16:52:55 +00:00
|
|
|
):
|
2026-02-02 23:44:27 -05:00
|
|
|
for product_lob_val in product_filled_value_list:
|
|
|
|
|
if "|" in product_lob_val:
|
|
|
|
|
product_lob_values = set(
|
|
|
|
|
v.strip()
|
|
|
|
|
for v in product_filled_value_list.split("|")
|
|
|
|
|
if v.strip() and v.strip() != "N/A"
|
|
|
|
|
)
|
|
|
|
|
elif (
|
|
|
|
|
product_lob_val.strip()
|
|
|
|
|
and product_lob_val.strip() != "N/A"
|
|
|
|
|
):
|
|
|
|
|
product_lob_values = {product_lob_val.strip()}
|
|
|
|
|
all_lob_values.update(product_lob_values)
|
2026-01-26 16:52:55 +00:00
|
|
|
|
2025-12-22 19:40:31 +00:00
|
|
|
# Set the merged, deduplicated value
|
|
|
|
|
if all_lob_values:
|
|
|
|
|
answer_dict["AARETE_DERIVED_LOB"] = "|".join(sorted(all_lob_values))
|
|
|
|
|
# Set relationship flags if values came from PROGRAM or PRODUCT
|
2026-01-22 18:12:35 +00:00
|
|
|
# Only set if field is empty or "N/A" (preserve LLM-determined Inclusive/Exclusive)
|
2025-12-22 19:40:31 +00:00
|
|
|
if program_lob_values:
|
2026-01-22 18:12:35 +00:00
|
|
|
existing_relationship = answer_dict.get("LOB_PROGRAM_RELATIONSHIP", "")
|
|
|
|
|
# Original code (CHANGED - now preserves LLM values):
|
|
|
|
|
# answer_dict["LOB_PROGRAM_RELATIONSHIP"] = "Exclusive"
|
|
|
|
|
if string_utils.is_empty(existing_relationship):
|
|
|
|
|
answer_dict["LOB_PROGRAM_RELATIONSHIP"] = "Exclusive"
|
2025-12-22 19:40:31 +00:00
|
|
|
if product_lob_values:
|
2026-01-22 18:12:35 +00:00
|
|
|
existing_relationship = answer_dict.get("LOB_PRODUCT_RELATIONSHIP", "")
|
|
|
|
|
# Original code (CHANGED - now preserves LLM values):
|
|
|
|
|
# answer_dict["LOB_PRODUCT_RELATIONSHIP"] = "Exclusive"
|
|
|
|
|
if string_utils.is_empty(existing_relationship):
|
|
|
|
|
answer_dict["LOB_PRODUCT_RELATIONSHIP"] = "Exclusive"
|
2025-12-22 19:40:31 +00:00
|
|
|
elif string_utils.is_empty(answer_dict.get("AARETE_DERIVED_LOB")):
|
|
|
|
|
# If still empty after all attempts, keep it as is (will be N/A or empty)
|
|
|
|
|
pass
|
2026-01-26 16:52:55 +00:00
|
|
|
|
2026-01-22 18:12:35 +00:00
|
|
|
# Handle cases where PROGRAM/PRODUCT is NA/blank (LLM was bypassed)
|
|
|
|
|
# Set relationship to "Exclusive" as default when PROGRAM/PRODUCT is missing and relationship is not set
|
|
|
|
|
# This covers both cases: LOB exists but PROGRAM/PRODUCT is NA, or both LOB and PROGRAM/PRODUCT are NA
|
|
|
|
|
if string_utils.is_empty(answer_dict.get("AARETE_DERIVED_PROGRAM")):
|
|
|
|
|
if string_utils.is_empty(answer_dict.get("LOB_PROGRAM_RELATIONSHIP")):
|
|
|
|
|
answer_dict["LOB_PROGRAM_RELATIONSHIP"] = "Exclusive"
|
2026-01-26 16:52:55 +00:00
|
|
|
|
2026-01-22 18:12:35 +00:00
|
|
|
if string_utils.is_empty(answer_dict.get("AARETE_DERIVED_PRODUCT")):
|
|
|
|
|
if string_utils.is_empty(answer_dict.get("LOB_PRODUCT_RELATIONSHIP")):
|
|
|
|
|
answer_dict["LOB_PRODUCT_RELATIONSHIP"] = "Exclusive"
|
2026-01-26 16:52:55 +00:00
|
|
|
|
2025-03-31 20:07:19 +00:00
|
|
|
return answer_dicts
|
2025-01-28 20:51:19 +00:00
|
|
|
|
2025-05-22 15:04:19 +00:00
|
|
|
|
2025-09-03 20:18:17 +00:00
|
|
|
def get_crosswalk_fields(answer_dicts: list, constants: Constants):
|
2026-02-03 00:07:12 -05:00
|
|
|
|
2025-03-31 20:07:19 +00:00
|
|
|
crosswalk_fields = FieldSet(file_path=config.FIELD_JSON_PATH, crosswalk=True)
|
2025-05-22 15:04:19 +00:00
|
|
|
for to_field in crosswalk_fields.fields:
|
2025-09-03 20:18:17 +00:00
|
|
|
to_field_name, from_field_name = to_field.field_name, to_field.base_field
|
|
|
|
|
# Find crosswalk
|
|
|
|
|
crosswalk = constants.get_constant(to_field.crosswalk)
|
|
|
|
|
if crosswalk:
|
|
|
|
|
for answer_dict in answer_dicts:
|
2026-02-03 00:07:12 -05:00
|
|
|
to_field_value = answer_dict.get(to_field_name)
|
|
|
|
|
from_field_value = answer_dict.get(from_field_name)
|
2025-09-03 20:18:17 +00:00
|
|
|
# If from_field_value is populated and to_field_value is not populated, perform mapping
|
2025-09-24 15:56:27 +00:00
|
|
|
if not string_utils.is_empty(
|
|
|
|
|
from_field_value
|
2026-02-03 00:07:12 -05:00
|
|
|
) and (string_utils.is_empty(to_field_value) or "N/A" in to_field_value):
|
2026-02-02 22:23:23 -05:00
|
|
|
if isinstance(from_field_value, list):
|
|
|
|
|
from_field_value_list = from_field_value
|
2025-10-07 21:59:31 +00:00
|
|
|
else:
|
|
|
|
|
from_field_value_list = [from_field_value]
|
|
|
|
|
|
|
|
|
|
# Map each value in the list
|
2025-10-07 15:06:10 +00:00
|
|
|
to_field_answer_list = []
|
|
|
|
|
for individual_from_field_value in from_field_value_list:
|
2026-01-26 16:52:55 +00:00
|
|
|
individual_from_field_value = (
|
|
|
|
|
individual_from_field_value.strip()
|
|
|
|
|
)
|
2025-10-07 15:06:10 +00:00
|
|
|
if individual_from_field_value in crosswalk.mapping.keys():
|
2025-12-10 20:01:52 +00:00
|
|
|
# Value is a key in the mapping - map it to the target value
|
2026-01-26 16:52:55 +00:00
|
|
|
to_field_answer_list.append(
|
|
|
|
|
crosswalk.mapping.get(individual_from_field_value)
|
|
|
|
|
)
|
2025-12-10 20:01:52 +00:00
|
|
|
elif individual_from_field_value in crosswalk.mapping.values():
|
|
|
|
|
# Value is already in the target format (e.g., "CHIP" when target is "CHIP")
|
|
|
|
|
# Keep it as-is for AARETE_DERIVED fields, or use reverse mapping for others
|
2025-10-07 15:06:10 +00:00
|
|
|
if "AARETE_DERIVED" in to_field_name:
|
|
|
|
|
to_field_answer_list.append(individual_from_field_value)
|
|
|
|
|
else:
|
2026-01-26 16:52:55 +00:00
|
|
|
to_field_answer_list.append(
|
|
|
|
|
crosswalk.create_reverse_mapping().get(
|
|
|
|
|
individual_from_field_value
|
|
|
|
|
)
|
|
|
|
|
)
|
2026-02-02 22:23:23 -05:00
|
|
|
answer_dict[to_field_name] = to_field_answer_list
|
2025-08-05 20:46:19 +00:00
|
|
|
return answer_dicts
|