Merged in feature/adding_contract_admenment_num_pc (pull request #863)

Feature/adding contract admenment num pc

* Updated contract amendment

* Format code with Black

* updated to have letters

* format fixes

* Adjusted unit testing

* Merge remote-tracking branch 'origin/DEV' into feature/adding_contract_admenment_num_pc

* apply formatting
This commit is contained in:
Siddhant Medar
2026-02-03 17:59:01 +00:00
parent 791558808a
commit 8fbe64e42e
8 changed files with 445 additions and 17 deletions
+1
View File
@@ -42,6 +42,7 @@ COL_EFF_DATE_RANK = "effective_date_rank"
COL_FINAL_RANK = "final_rank"
COL_CHILD_RANK = "child_rank"
COL_CHILD_INDEX = "child_index"
COL_AMENDMENT_NUM = "AARETE_DERIVED_AMENDMENT_NUM"
# Crosswalk columns
XWALK_COL_FILE_NAME = "File Name"
-1
View File
@@ -172,7 +172,6 @@ if __name__ == "__main__":
)
# Generate timestamp for this run
run_timestamp = f"run_{datetime.now().strftime('%Y%m%d_%H-%M')}_{config.BATCH_ID}"
if config.DOCZY_OUTPUT_FOR_PC:
results = main(config.DOCZY_OUTPUT_FOR_PC)
logging.info(f"PC Mapping complete. Processed {results} files.")
+6 -1
View File
@@ -57,6 +57,7 @@ from src.constants.parent_child.bcbs import (
NON_PARENT_KEYWORDS,
DATE_PATTERNS,
IRS_GROUP_REPLACEMENTS,
COL_AMENDMENT_NUM,
)
logger = logging.getLogger(__name__)
@@ -91,6 +92,7 @@ OUTPUT_COLUMNS = [
COL_CONTRACT_EFF_DATE,
"File Name",
"Provider Type_Consolidated",
"AARETE_DERIVED_AMENDMENT_NUM",
]
# ============================================================================
@@ -1025,9 +1027,12 @@ def bcbs_main(df_read, xwalk_path=None):
# Step 28: Add File Name and Provider Type_Consolidated columns
subset["File Name"] = subset[COL_CONTRACT_NAME]
subset["Provider Type_Consolidated"] = subset[COL_CONSOLIDATED_PROVIDER]
if COL_AMENDMENT_NUM in subset.columns:
subset["AARETE_DERIVED_AMENDMENT_NUM"] = subset[COL_AMENDMENT_NUM]
# Step 29: Select output columns and export
subset = subset[OUTPUT_COLUMNS]
output_cols = [col for col in OUTPUT_COLUMNS if col in subset.columns]
subset = subset[output_cols]
subset = subset.sort_values(COL_FOLDER)
logger.info(f"Total records: {len(subset)}")
logger.info(f"Parents: {subset[COL_IS_PARENT].sum()}")
+7
View File
@@ -34,6 +34,7 @@ class ColumnMapper:
r"^contract.*title$",
r"^title$",
r"^contract.*name.*title$",
r"^contract.*name$",
r"^agreement.*title$",
r"^doc.*title$",
],
@@ -51,6 +52,7 @@ class ColumnMapper:
r"^provider.*name$",
r"^prov.*name$",
r"^group.*name$",
r"^multiple.*irs.*names$",
],
"PROV_GROUP_TIN": [
r"^prov.*group.*tin$",
@@ -105,6 +107,10 @@ class ColumnMapper:
r"^plan.*state$",
r"^health.*plan.*state$",
],
"AARETE_DERIVED_AMENDMENT_NUM": [
r"aarete_derived_amendment_num",
r"^amendment.*number$",
],
}
# Optional columns (won't fail if not found)
@@ -113,6 +119,7 @@ class ColumnMapper:
"SERVICE_TERM",
"PROV_GROUP_TIN",
"PROV_GROUP_NPI",
"AARETE_DERIVED_AMENDMENT_NUM",
]
def __init__(self, df: pd.DataFrame):
+20 -8
View File
@@ -771,35 +771,45 @@ def assign_child_ranks(df, grouper_col="grouping_key"):
children.at[idx, "_parent_identity"] = f"unknown_{assigned_rank}"
# Group by parent identity and assign sequential ranks
has_amendment_col = "AARETE_DERIVED_AMENDMENT_NUM" in children.columns
for parent_id, grp_df in children.groupby("_parent_identity"):
if parent_id == "orphan":
base_rank = "0.0"
parent_rank_str = "0"
else:
# Extract parent_rank from the child's assigned_parent_rank
assigned_rank = grp_df.iloc[0]["assigned_parent_rank"]
# Convert to int if it's a float (2.0 -> 2), then add .0
# Convert to int if it's a float (2.0 -> 2)
if pd.notna(assigned_rank) and assigned_rank != ASSIGNMENT_NO_PARENT:
try:
if isinstance(assigned_rank, float):
base_rank = f"{int(assigned_rank)}.0"
parent_rank_str = str(int(assigned_rank))
else:
base_rank = f"{assigned_rank}.0"
parent_rank_str = str(assigned_rank)
except Exception as e:
logging.debug(
f"Could not convert rank '{assigned_rank}' to int: {e}"
)
base_rank = f"{assigned_rank}.0"
parent_rank_str = str(assigned_rank)
else:
base_rank = "0.0"
parent_rank_str = "0"
# Sort by effective date (already converted to datetime at function start)
grp_df = grp_df.copy()
grp_df["sort_date"] = grp_df["fixed_effective_date"].fillna(pd.Timestamp.max)
grp_df = grp_df.sort_values("sort_date")
# Assign sequential ranks
# Assign sequential ranks using amendment number instead of 0
for i, idx in enumerate(grp_df.index, start=1):
rank_val = f"{base_rank}.{i}"
amendment_val = 0
if has_amendment_col:
raw = grp_df.at[idx, "AARETE_DERIVED_AMENDMENT_NUM"]
if pd.notna(raw) and str(raw).strip() != "":
try:
amendment_val = int(float(raw))
except (ValueError, TypeError):
amendment_val = raw
rank_val = f"{parent_rank_str}.{amendment_val}.{i}"
children.loc[idx, "child_rank"] = rank_val
# Clean up temp column
@@ -1044,6 +1054,7 @@ def parent_child_mapping(
cols_order = [
"grouping_key",
"PROV_GROUP_NAME_FULL_cleaned",
"AARETE_DERIVED_AMENDMENT_NUM",
"consolidated_lob",
"fixed_effective_date",
"parent_child_flag",
@@ -1110,6 +1121,7 @@ def parent_child_mapping(
"PROV_GROUP_NPI",
"payer_name_cleaned",
"PAYER_STATE",
"AARETE_DERIVED_AMENDMENT_NUM",
"grouping_key",
"parent",
"combined_rank",
+39 -5
View File
@@ -1793,6 +1793,7 @@ class ConfigFactory:
"cnc": ConfigFactory.create_molina, # CNC uses same config as Molina
"caresource": ConfigFactory.create_caresource,
"bcbs": ConfigFactory.create_bcbs,
"bcbsnc": ConfigFactory.create_bcbs,
"clover_health": ConfigFactory.create_clover,
}
@@ -2020,6 +2021,20 @@ class ParentChildEngine:
parent_cache = df.loc[df["is_parent"], cols_needed].to_dict("index")
return self.child_assigner.assign_children(df, self.cfg, parent_cache)
@staticmethod
def _normalize_amendment_val(x):
"""Convert amendment value to string for rank construction.
Handles both numeric values (e.g. 3.0 -> '3') and character
values (e.g. 'A' -> 'A'). Returns '0' for NaN / empty.
"""
if pd.notna(x) and str(x).strip() != "":
try:
return str(int(float(x)))
except (ValueError, TypeError):
return str(x).strip()
return "0"
def prepare_output(self, df: pd.DataFrame) -> pd.DataFrame:
"""Initialize output columns."""
df["parent_child_flag_dest"] = pd.Series([pd.NA] * len(df), dtype="string")
@@ -2060,12 +2075,23 @@ class ParentChildEngine:
parent_idx = tmp["assigned_parent_idx"].astype("int64").values
parent_rank_str = (
df.loc[parent_idx, "parent_rank"]
.astype("string")
.apply(lambda x: str(int(x)) if pd.notna(x) else "0")
.reset_index(drop=True)
.values
)
df.loc[tmp.index, "child_rank_dest"] = parent_rank_str + ".0." + seq_str
if "AARETE_DERIVED_AMENDMENT_NUM" in df.columns:
amendment_str = (
df.loc[tmp.index, "AARETE_DERIVED_AMENDMENT_NUM"]
.apply(self._normalize_amendment_val)
.reset_index(drop=True)
.values
)
else:
amendment_str = "0"
df.loc[tmp.index, "child_rank_dest"] = (
parent_rank_str + "." + amendment_str + "." + seq_str
)
df.loc[tmp.index, "combined_rank_dest"] = df.loc[
tmp.index, "child_rank_dest"
]
@@ -2079,9 +2105,17 @@ class ParentChildEngine:
tmp = df.loc[orphan_mask, ["input_row_order"]].copy()
tmp = tmp.sort_values(["input_row_order"], kind="mergesort")
seq = pd.Series(np.arange(1, len(tmp) + 1, dtype=np.int32), index=tmp.index)
df.loc[tmp.index, "orphan_rank_dest"] = (
"0.0." + seq.astype("string")
).values
if "AARETE_DERIVED_AMENDMENT_NUM" in df.columns:
amendment_str = df.loc[tmp.index, "AARETE_DERIVED_AMENDMENT_NUM"].apply(
self._normalize_amendment_val
)
df.loc[tmp.index, "orphan_rank_dest"] = (
"0." + amendment_str + "." + seq.astype("string")
).values
else:
df.loc[tmp.index, "orphan_rank_dest"] = (
"0.0." + seq.astype("string")
).values
df.loc[tmp.index, "combined_rank_dest"] = df.loc[
tmp.index, "orphan_rank_dest"
]
+1
View File
@@ -1552,6 +1552,7 @@ Return NO if EITHER term contains (even if valid payment method exists):
"""
def VALIDATE_REIMBURSEMENTS_PROMPT(service_term: str, reimb_term: str) -> str:
"""Returns ONLY dynamic content for validation.
Call VALIDATE_REIMBURSEMENTS_INSTRUCTION() separately for the cached instruction.
+371 -2
View File
@@ -34,6 +34,7 @@ from src.parent_child.qc import (
resolve_column,
TextProcessor,
ConfigFactory,
ParentChildEngine,
)
@@ -526,7 +527,8 @@ class TestAssignChildRanks(unittest.TestCase):
self.assertTrue(result["child_rank"].iloc[1].startswith("0.0"))
def test_assigned_children_get_parent_prefix(self):
"""Children assigned to parent get ranks with parent's rank prefix."""
"""Children assigned to parent get ranks with parent's rank prefix.
Without AARETE_DERIVED_AMENDMENT_NUM column, amendment defaults to 0."""
df = pd.DataFrame(
{
"grouping_key": ["TIN:123", "TIN:123", "TIN:123"],
@@ -540,7 +542,7 @@ class TestAssignChildRanks(unittest.TestCase):
# Parent should have combined_rank "1"
self.assertEqual(result["combined_rank"].iloc[0], "1")
# Children should have ranks like "1.0.1", "1.0.2"
# Children should have ranks like "1.0.1", "1.0.2" (0 = default amendment)
child_ranks = result[result["parent"] == False]["child_rank"].tolist()
self.assertTrue(all(r.startswith("1.0.") for r in child_ranks))
@@ -637,5 +639,372 @@ class TestConfigFactory(unittest.TestCase):
self.assertIsNotNone(config)
class TestAssignChildRanksWithAmendmentNum(unittest.TestCase):
"""Tests for assign_child_ranks with AARETE_DERIVED_AMENDMENT_NUM column."""
def test_children_use_amendment_num_in_rank(self):
"""Children with amendment number get parent_rank.amendment_num.seq format."""
df = pd.DataFrame(
{
"grouping_key": ["TIN:123", "TIN:123", "TIN:123"],
"parent": [True, False, False],
"parent_rank": [1.0, np.nan, np.nan],
"assigned_parent_rank": [np.nan, 1.0, 1.0],
"fixed_effective_date": ["2024-01-01", "2024-03-01", "2024-02-01"],
"AARETE_DERIVED_AMENDMENT_NUM": [np.nan, 5, 3],
}
)
result = assign_child_ranks(df)
child_df = result[result["parent"] == False].sort_values("fixed_effective_date")
ranks = child_df["child_rank"].tolist()
# Earlier date (index 2, amendment=3) sorted first: 1.3.1
# Later date (index 1, amendment=5) sorted second: 1.5.2
self.assertEqual(ranks[0], "1.3.1")
self.assertEqual(ranks[1], "1.5.2")
def test_children_with_nan_amendment_default_to_0(self):
"""Children with NaN/empty amendment number default to 0."""
df = pd.DataFrame(
{
"grouping_key": ["TIN:123", "TIN:123", "TIN:123"],
"parent": [True, False, False],
"parent_rank": [1.0, np.nan, np.nan],
"assigned_parent_rank": [np.nan, 1.0, 1.0],
"fixed_effective_date": ["2024-01-01", "2024-03-01", "2024-02-01"],
"AARETE_DERIVED_AMENDMENT_NUM": [np.nan, np.nan, ""],
}
)
result = assign_child_ranks(df)
child_df = result[result["parent"] == False].sort_values("fixed_effective_date")
ranks = child_df["child_rank"].tolist()
# Both should default to 0 for amendment part
self.assertEqual(ranks[0], "1.0.1")
self.assertEqual(ranks[1], "1.0.2")
def test_orphans_use_amendment_num_in_rank(self):
"""Orphans with amendment number get 0.amendment_num.seq format."""
df = pd.DataFrame(
{
"grouping_key": ["TIN:123", "TIN:123"],
"parent": [False, False],
"assigned_parent_rank": ["no_parent", "no_parent"],
"fixed_effective_date": ["2024-01-01", "2024-02-01"],
"AARETE_DERIVED_AMENDMENT_NUM": [2, 7],
}
)
result = assign_child_ranks(df)
ranks = result["child_rank"].tolist()
self.assertEqual(ranks[0], "0.2.1")
self.assertEqual(ranks[1], "0.7.2")
def test_mixed_amendment_values_in_same_group(self):
"""Different amendment numbers within same parent group."""
df = pd.DataFrame(
{
"grouping_key": ["TIN:123", "TIN:123", "TIN:123", "TIN:123"],
"parent": [True, False, False, False],
"parent_rank": [1.0, np.nan, np.nan, np.nan],
"assigned_parent_rank": [np.nan, 1.0, 1.0, 1.0],
"fixed_effective_date": [
"2024-01-01",
"2024-02-01",
"2024-03-01",
"2024-04-01",
],
"AARETE_DERIVED_AMENDMENT_NUM": [np.nan, 3, np.nan, 5],
}
)
result = assign_child_ranks(df)
child_df = result[result["parent"] == False].sort_values("fixed_effective_date")
ranks = child_df["child_rank"].tolist()
self.assertEqual(ranks[0], "1.3.1") # amendment=3
self.assertEqual(ranks[1], "1.0.2") # amendment=NaN -> 0
self.assertEqual(ranks[2], "1.5.3") # amendment=5
def test_no_amendment_column_backward_compatible(self):
"""Without AARETE_DERIVED_AMENDMENT_NUM column, uses 0 (backward compat)."""
df = pd.DataFrame(
{
"grouping_key": ["TIN:123", "TIN:123"],
"parent": [True, False],
"parent_rank": [1.0, np.nan],
"assigned_parent_rank": [np.nan, 1.0],
"fixed_effective_date": ["2024-01-01", "2024-03-01"],
}
)
result = assign_child_ranks(df)
child_rank = result[result["parent"] == False]["child_rank"].iloc[0]
self.assertEqual(child_rank, "1.0.1")
def test_float_amendment_num_converted_to_int(self):
"""Float amendment numbers like 3.0 are converted to int 3."""
df = pd.DataFrame(
{
"grouping_key": ["TIN:123", "TIN:123"],
"parent": [True, False],
"parent_rank": [1.0, np.nan],
"assigned_parent_rank": [np.nan, 1.0],
"fixed_effective_date": ["2024-01-01", "2024-03-01"],
"AARETE_DERIVED_AMENDMENT_NUM": [np.nan, 3.0],
}
)
result = assign_child_ranks(df)
child_rank = result[result["parent"] == False]["child_rank"].iloc[0]
self.assertEqual(child_rank, "1.3.1")
def test_parent_combined_rank_unchanged(self):
"""Parent combined_rank should still be just the parent rank number."""
df = pd.DataFrame(
{
"grouping_key": ["TIN:123", "TIN:123"],
"parent": [True, False],
"parent_rank": [1.0, np.nan],
"assigned_parent_rank": [np.nan, 1.0],
"fixed_effective_date": ["2024-01-01", "2024-03-01"],
"AARETE_DERIVED_AMENDMENT_NUM": [np.nan, 5],
}
)
result = assign_child_ranks(df)
parent_rank = result[result["parent"] == True]["combined_rank"].iloc[0]
self.assertEqual(parent_rank, "1")
def test_children_use_character_amendment_num_in_rank(self):
"""Children with character amendment number (A, B, C) use it in rank."""
df = pd.DataFrame(
{
"grouping_key": ["TIN:123", "TIN:123", "TIN:123"],
"parent": [True, False, False],
"parent_rank": [1.0, np.nan, np.nan],
"assigned_parent_rank": [np.nan, 1.0, 1.0],
"fixed_effective_date": ["2024-01-01", "2024-03-01", "2024-02-01"],
"AARETE_DERIVED_AMENDMENT_NUM": [np.nan, "B", "A"],
}
)
result = assign_child_ranks(df)
child_df = result[result["parent"] == False].sort_values("fixed_effective_date")
ranks = child_df["child_rank"].tolist()
self.assertEqual(ranks[0], "1.A.1") # Amendment A, sequence 1
self.assertEqual(ranks[1], "1.B.2") # Amendment B, sequence 2
def test_orphans_use_character_amendment_num_in_rank(self):
"""Orphans with character amendment number get 0.amendment.seq format."""
df = pd.DataFrame(
{
"grouping_key": ["TIN:123", "TIN:123"],
"parent": [False, False],
"assigned_parent_rank": ["no_parent", "no_parent"],
"fixed_effective_date": ["2024-01-01", "2024-02-01"],
"AARETE_DERIVED_AMENDMENT_NUM": ["A", "C"],
}
)
result = assign_child_ranks(df)
ranks = result["child_rank"].tolist()
self.assertEqual(ranks[0], "0.A.1")
self.assertEqual(ranks[1], "0.C.2")
def test_mixed_numeric_and_character_amendment_nums(self):
"""Mix of numeric and character amendment numbers in same group."""
df = pd.DataFrame(
{
"grouping_key": ["TIN:123", "TIN:123", "TIN:123", "TIN:123"],
"parent": [True, False, False, False],
"parent_rank": [1.0, np.nan, np.nan, np.nan],
"assigned_parent_rank": [np.nan, 1.0, 1.0, 1.0],
"fixed_effective_date": [
"2024-01-01",
"2024-02-01",
"2024-03-01",
"2024-04-01",
],
"AARETE_DERIVED_AMENDMENT_NUM": [np.nan, 3, "A", 5],
}
)
result = assign_child_ranks(df)
child_df = result[result["parent"] == False].sort_values("fixed_effective_date")
ranks = child_df["child_rank"].tolist()
self.assertEqual(ranks[0], "1.3.1") # numeric 3
self.assertEqual(ranks[1], "1.A.2") # character A
self.assertEqual(ranks[2], "1.5.3") # numeric 5
class TestComputeRanksWithAmendmentNum(unittest.TestCase):
"""Tests for ParentChildEngine.compute_ranks with AARETE_DERIVED_AMENDMENT_NUM."""
def _make_engine(self):
"""Create a minimal ParentChildEngine for testing compute_ranks."""
config = ConfigFactory.get_config("molina")
return ParentChildEngine(config.to_dict())
def _make_base_df(self, extra_cols=None):
"""Create a base DataFrame with parent, child, and orphan rows.
Index 0: Parent (parent_rank=1)
Index 1: Child assigned to parent 0
Index 2: Child assigned to parent 0
Index 3: Orphan
"""
df = pd.DataFrame(
{
"is_parent": [True, False, False, False],
"parent_rank": [1, np.nan, np.nan, np.nan],
"assigned_parent_idx": [pd.NA, 0, 0, pd.NA],
"eff_date": pd.to_datetime(
["2024-01-01", "2024-03-01", "2024-02-01", "2024-04-01"]
),
"input_row_order": [0, 1, 2, 3],
}
)
df["assigned_parent_idx"] = df["assigned_parent_idx"].astype("Int64")
if extra_cols:
for col, vals in extra_cols.items():
df[col] = vals
# Initialize output columns
df["parent_child_flag_dest"] = pd.Series([pd.NA] * len(df), dtype="string")
df["parent_rank_dest"] = pd.Series([pd.NA] * len(df), dtype="string")
df["child_rank_dest"] = pd.Series([pd.NA] * len(df), dtype="string")
df["orphan_rank_dest"] = pd.Series([pd.NA] * len(df), dtype="string")
df["combined_rank_dest"] = pd.Series([pd.NA] * len(df), dtype="string")
return df
def test_child_rank_dest_with_amendment_num(self):
"""child_rank_dest uses amendment number instead of 0."""
engine = self._make_engine()
df = self._make_base_df(
extra_cols={
"AARETE_DERIVED_AMENDMENT_NUM": [np.nan, 5, 3, 2],
}
)
result = engine.compute_ranks(df)
# Children sorted by (assigned_parent_idx, eff_date, input_row_order)
# Index 2 (eff 2024-02-01, amendment=3) comes first -> seq 1
# Index 1 (eff 2024-03-01, amendment=5) comes second -> seq 2
self.assertEqual(result.at[2, "child_rank_dest"], "1.3.1")
self.assertEqual(result.at[1, "child_rank_dest"], "1.5.2")
def test_child_rank_dest_without_amendment_column(self):
"""child_rank_dest defaults to 0 when column is missing.
Format: parent_rank.0.seq (backward compatible)."""
engine = self._make_engine()
df = self._make_base_df() # No amendment column
result = engine.compute_ranks(df)
self.assertEqual(result.at[2, "child_rank_dest"], "1.0.1")
self.assertEqual(result.at[1, "child_rank_dest"], "1.0.2")
def test_child_rank_dest_with_nan_amendment(self):
"""child_rank_dest defaults to 0 for NaN amendment values."""
engine = self._make_engine()
df = self._make_base_df(
extra_cols={
"AARETE_DERIVED_AMENDMENT_NUM": [np.nan, np.nan, np.nan, np.nan],
}
)
result = engine.compute_ranks(df)
# NaN amendment defaults to 0, so same as without the column
self.assertEqual(result.at[2, "child_rank_dest"], "1.0.1")
self.assertEqual(result.at[1, "child_rank_dest"], "1.0.2")
def test_orphan_rank_dest_with_amendment_num(self):
"""orphan_rank_dest uses amendment number instead of 0."""
engine = self._make_engine()
df = self._make_base_df(
extra_cols={
"AARETE_DERIVED_AMENDMENT_NUM": [np.nan, 5, 3, 7],
}
)
result = engine.compute_ranks(df)
# Index 3 is orphan with amendment=7
self.assertEqual(result.at[3, "orphan_rank_dest"], "0.7.1")
def test_orphan_rank_dest_without_amendment_column(self):
"""orphan_rank_dest defaults to 0 when column is missing."""
engine = self._make_engine()
df = self._make_base_df() # No amendment column
result = engine.compute_ranks(df)
self.assertEqual(result.at[3, "orphan_rank_dest"], "0.0.1")
def test_combined_rank_dest_matches_child_and_orphan(self):
"""combined_rank_dest equals child_rank_dest for children and orphan_rank_dest for orphans."""
engine = self._make_engine()
df = self._make_base_df(
extra_cols={
"AARETE_DERIVED_AMENDMENT_NUM": [np.nan, 5, 3, 7],
}
)
result = engine.compute_ranks(df)
# Children: combined_rank_dest == child_rank_dest
self.assertEqual(
result.at[1, "combined_rank_dest"], result.at[1, "child_rank_dest"]
)
self.assertEqual(
result.at[2, "combined_rank_dest"], result.at[2, "child_rank_dest"]
)
# Orphan: combined_rank_dest == orphan_rank_dest
self.assertEqual(
result.at[3, "combined_rank_dest"], result.at[3, "orphan_rank_dest"]
)
def test_child_rank_dest_with_character_amendment_num(self):
"""child_rank_dest handles character amendment values (A, B, C)."""
engine = self._make_engine()
df = self._make_base_df(
extra_cols={
"AARETE_DERIVED_AMENDMENT_NUM": [np.nan, "B", "A", "C"],
}
)
result = engine.compute_ranks(df)
# Index 2 (eff 2024-02-01, amendment=A) comes first -> seq 1
# Index 1 (eff 2024-03-01, amendment=B) comes second -> seq 2
self.assertEqual(result.at[2, "child_rank_dest"], "1.A.1")
self.assertEqual(result.at[1, "child_rank_dest"], "1.B.2")
def test_orphan_rank_dest_with_character_amendment_num(self):
"""orphan_rank_dest handles character amendment values."""
engine = self._make_engine()
df = self._make_base_df(
extra_cols={
"AARETE_DERIVED_AMENDMENT_NUM": [np.nan, 5, 3, "A"],
}
)
result = engine.compute_ranks(df)
self.assertEqual(result.at[3, "orphan_rank_dest"], "0.A.1")
def test_child_rank_dest_with_mixed_numeric_and_character_amendment(self):
"""child_rank_dest handles mix of numeric and character amendment values."""
engine = self._make_engine()
df = self._make_base_df(
extra_cols={
"AARETE_DERIVED_AMENDMENT_NUM": [np.nan, "A", 3, "B"],
}
)
result = engine.compute_ranks(df)
# Index 2 (eff 2024-02-01, amendment=3) -> seq 1
# Index 1 (eff 2024-03-01, amendment=A) -> seq 2
self.assertEqual(result.at[2, "child_rank_dest"], "1.3.1")
self.assertEqual(result.at[1, "child_rank_dest"], "1.A.2")
# Index 3 is orphan with amendment=B
self.assertEqual(result.at[3, "orphan_rank_dest"], "0.B.1")
if __name__ == "__main__":
unittest.main()