diff --git a/src/parent_child/qc.py b/src/parent_child/qc.py index b0407c1..8caf8a3 100644 --- a/src/parent_child/qc.py +++ b/src/parent_child/qc.py @@ -2109,8 +2109,9 @@ class ParentChildEngine: orphan_mask = (~pm) & (~cm) if orphan_mask.any(): - tmp = df.loc[orphan_mask, ["input_row_order"]].copy() - tmp = tmp.sort_values(["input_row_order"], kind="mergesort") + tmp = df.loc[orphan_mask, ["eff_date", "input_row_order"]].copy() + tmp["eff_sort"] = tmp["eff_date"].fillna(pd.Timestamp.max) + tmp = tmp.sort_values(["eff_sort", "input_row_order"], kind="mergesort") seq = pd.Series(np.arange(1, len(tmp) + 1, dtype=np.int32), index=tmp.index) if "AARETE_DERIVED_AMENDMENT_NUM" in df.columns: amendment_str = df.loc[tmp.index, "AARETE_DERIVED_AMENDMENT_NUM"].apply( diff --git a/src/tests/test_parent_child.py b/src/tests/test_parent_child.py index 59ebaa3..0e1527f 100644 --- a/src/tests/test_parent_child.py +++ b/src/tests/test_parent_child.py @@ -1006,5 +1006,72 @@ class TestComputeRanksWithAmendmentNum(unittest.TestCase): self.assertEqual(result.at[3, "orphan_rank_dest"], "0.2.1") # B=2 +class TestNormalizeAmendmentVal(unittest.TestCase): + """Unit tests for ParentChildEngine._normalize_amendment_val.""" + + def test_numeric_int(self): + self.assertEqual(ParentChildEngine._normalize_amendment_val(3), "3") + + def test_numeric_float(self): + self.assertEqual(ParentChildEngine._normalize_amendment_val(3.0), "3") + + def test_numeric_string(self): + self.assertEqual(ParentChildEngine._normalize_amendment_val("5"), "5") + + def test_numeric_float_string(self): + self.assertEqual(ParentChildEngine._normalize_amendment_val("7.0"), "7") + + def test_letter_A_upper(self): + self.assertEqual(ParentChildEngine._normalize_amendment_val("A"), "1") + + def test_letter_B_upper(self): + self.assertEqual(ParentChildEngine._normalize_amendment_val("B"), "2") + + def test_letter_C_upper(self): + self.assertEqual(ParentChildEngine._normalize_amendment_val("C"), "3") + + def test_letter_F_upper(self): + self.assertEqual(ParentChildEngine._normalize_amendment_val("F"), "6") + + def test_letter_Z_upper(self): + self.assertEqual(ParentChildEngine._normalize_amendment_val("Z"), "26") + + def test_letter_a_lower(self): + """Lowercase letters are also converted (a=1).""" + self.assertEqual(ParentChildEngine._normalize_amendment_val("a"), "1") + + def test_letter_b_lower(self): + self.assertEqual(ParentChildEngine._normalize_amendment_val("b"), "2") + + def test_letter_f_lower(self): + self.assertEqual(ParentChildEngine._normalize_amendment_val("f"), "6") + + def test_nan_returns_zero(self): + self.assertEqual(ParentChildEngine._normalize_amendment_val(np.nan), "0") + + def test_none_returns_zero(self): + self.assertEqual(ParentChildEngine._normalize_amendment_val(None), "0") + + def test_empty_string_returns_zero(self): + self.assertEqual(ParentChildEngine._normalize_amendment_val(""), "0") + + def test_whitespace_only_returns_zero(self): + self.assertEqual(ParentChildEngine._normalize_amendment_val(" "), "0") + + def test_multi_char_string_returned_as_is(self): + """Multi-character non-numeric strings are returned as-is.""" + self.assertEqual(ParentChildEngine._normalize_amendment_val("AB"), "AB") + + def test_letter_with_whitespace(self): + """Letter with surrounding whitespace is still converted.""" + self.assertEqual(ParentChildEngine._normalize_amendment_val(" A "), "1") + + def test_zero_value(self): + self.assertEqual(ParentChildEngine._normalize_amendment_val(0), "0") + + def test_negative_number(self): + self.assertEqual(ParentChildEngine._normalize_amendment_val(-1), "-1") + + if __name__ == "__main__": unittest.main()