Files
IPSExtractor/tests/test_csv_cleaning.py
T

348 lines
9.6 KiB
Python

from __future__ import annotations
import pandas as pd
from application.csv_cleaning import (
apply_rules,
homologate_doc_type,
split_doc_tipo_y_numero_texto,
)
def test_homologate_doc_type_aliases() -> None:
cases = {
"Cedula": "CC",
"C.C": "CC",
"C C": "CC",
"Registro Civil": "RC",
"Salvoconducto": "SC",
"TC": "RC",
"C\u00e9dula de Extranjeria": "CE",
}
for raw, expected in cases.items():
assert homologate_doc_type(raw) == expected
def test_split_doc_type_with_long_names() -> None:
cases = {
"Cedula 123.456": ("CC", "123456"),
"Registro Civil: 001-23": ("RC", "00123"),
"Permiso especial de permanencia 999": ("PE", "999"),
"Permiso por proteccion Temporal / 555": ("PT", "555"),
}
for raw, expected in cases.items():
assert split_doc_tipo_y_numero_texto(raw) == expected
def test_split_doc_type_with_abbreviations_and_noise() -> None:
cases = {
"C.C-123": ("CC", "123"),
"TL_987": ("TI", "987"),
"CE/1.234.567": ("CE", "1234567"),
"T1 # 44": ("TI", "44"),
}
for raw, expected in cases.items():
assert split_doc_tipo_y_numero_texto(raw) == expected
def test_split_unknown_type_preserves_clean_uppercase() -> None:
assert split_doc_tipo_y_numero_texto("ABC Especial: 123-45") == (
"ABC ESPECIAL",
"12345",
)
def test_split_without_homologation_preserves_clean_type() -> None:
assert split_doc_tipo_y_numero_texto(
"Cedula 123.456",
homologate=False,
) == ("CEDULA", "123456")
def test_apply_rules_homologates_type_column_only() -> None:
df = pd.DataFrame(
{
"tipo": [
"Cedula",
"C.C",
"Registro Civil",
"Salvoconducto",
"TC",
"Extranjera",
]
}
)
result, log = apply_rules(df, {"tipo": ["doc_type_homologate"]})
assert result["tipo"].tolist() == ["CC", "CC", "RC", "SC", "RC", "CE"]
assert any("Homologar tipo de documento" in line for line in log)
def test_apply_rules_split_uses_homologation_when_selected() -> None:
df = pd.DataFrame(
{
"documento": [
"Cedula 123.456",
"Registro Civil: 001-23",
"ABC Especial: 123-45",
]
}
)
result, _log = apply_rules(
df,
{"documento": ["doc_id_split_tipo_numero", "doc_type_homologate"]},
)
assert result["documento_doc_tipo"].tolist() == ["CC", "RC", "ABC ESPECIAL"]
assert result["documento_doc_numeros"].tolist() == ["123456", "00123", "12345"]
assert result["documento"].tolist() == df["documento"].tolist()
def test_apply_rules_split_can_skip_homologation() -> None:
df = pd.DataFrame({"documento": ["Cedula 123.456"]})
result, _log = apply_rules(df, {"documento": ["doc_id_split_tipo_numero"]})
assert result["documento_doc_tipo"].tolist() == ["CEDULA"]
assert result["documento_doc_numeros"].tolist() == ["123456"]
def test_apply_rules_unified_date_rule_numeric_and_separated() -> None:
df = pd.DataFrame(
{
"fecha": [
"20260426",
"26042026",
"260426",
"26/04/2026",
"04/26/2026",
"2026-04-26 10:30",
]
}
)
result, log = apply_rules(df, {"fecha": ["date_to_ddmmyyyy"]})
assert result["fecha"].tolist() == [
"26/04/2026",
"26/04/2026",
"26/04/2026",
"26/04/2026",
"26/04/2026",
"26/04/2026",
]
assert any("Normalizar fecha" in line for line in log)
def test_apply_rules_unified_date_rule_text_months() -> None:
df = pd.DataFrame(
{
"fecha": [
"26-abril-2026",
"26 abril 2026",
"26/abr/2026",
"abril 26 2026",
"2026 abril 26",
"15-sept-25",
"07 setiembre 2024",
]
}
)
result, _log = apply_rules(df, {"fecha": ["date_to_ddmmyyyy"]})
assert result["fecha"].tolist() == [
"26/04/2026",
"26/04/2026",
"26/04/2026",
"26/04/2026",
"26/04/2026",
"15/09/2025",
"07/09/2024",
]
def test_apply_rules_unified_date_spanish_abbr_month_with_time() -> None:
df = pd.DataFrame(
{
"fecha": [
"15/may./2024 13:44",
"22/may./2024 11:09",
"30/04/2024",
]
}
)
result, _log = apply_rules(df, {"fecha": ["date_to_ddmmyyyy"]})
assert result["fecha"].tolist() == [
"15/05/2024",
"22/05/2024",
"30/04/2024",
]
def test_apply_rules_date_after_strip_special_spaces_glued_year_time() -> None:
df = pd.DataFrame({"fecha": ["15/may./2024 13:44"]})
result, _log = apply_rules(
df,
{"fecha": ["strip_special_spaces", "date_to_ddmmyyyy"]},
)
assert result["fecha"].tolist() == ["15/05/2024"]
def test_apply_rules_keep_date_seps_preserves_time_then_normalizes() -> None:
df = pd.DataFrame({"fecha": ["30/04/2024 13:44"]})
result, _log = apply_rules(
df,
{"fecha": ["strip_special_keep_date_seps", "date_to_ddmmyyyy"]},
)
assert result["fecha"].tolist() == ["30/04/2024"]
def test_apply_rules_us_style_date_comma_then_am_pm_strips_time() -> None:
"""Formato tipo Excel/locale US: mes/día/año, coma y hora — solo fecha DD/MM/AAAA."""
df = pd.DataFrame({"fecha": ["12/27/24, 12:00 AM"]})
result, _log = apply_rules(df, {"fecha": ["date_to_ddmmyyyy"]})
assert result["fecha"].tolist() == ["27/12/2024"]
def test_apply_rules_unified_date_rule_noise_and_unparseable_values() -> None:
df = pd.DataFrame(
{
"fecha": [
"**26_abril_2026++",
"Fecha: 26-abril-2026",
"xx26/04/2026yy",
"no es fecha",
"31-febrero-2026",
]
}
)
result, _log = apply_rules(df, {"fecha": ["date_to_ddmmyyyy"]})
assert result["fecha"].tolist() == [
"26/04/2026",
"26/04/2026",
"26/04/2026",
"no es fecha",
"31-febrero-2026",
]
def test_apply_rules_cop_mod_moderadora_matches_year() -> None:
df = pd.DataFrame(
{"Fecha Servicio": ["01/01/2024"], "Importe": [4500]},
)
result, _log = apply_rules(
df,
{"Importe": ["cop_mod_split"]},
cop_mod_fecha_column={"Importe": "Fecha Servicio"},
cop_mod_config={"moderadora_by_year": {"2024": [4500, 18200, 47700]}},
)
assert result["Importe_moderadora"].tolist() == [4500]
assert result["Importe_copago"].tolist() == [0]
def test_apply_rules_cop_mod_non_moderadora_to_copago() -> None:
df = pd.DataFrame(
{"Fecha Servicio": ["01/01/2024"], "Importe": [4982]},
)
result, _log = apply_rules(
df,
{"Importe": ["cop_mod_split"]},
cop_mod_fecha_column={"Importe": "Fecha Servicio"},
cop_mod_config={"moderadora_by_year": {"2024": [4500, 18200, 47700]}},
)
assert result["Importe_copago"].tolist() == [4982]
assert result["Importe_moderadora"].tolist() == [0]
def test_apply_rules_cop_mod_two_phase_dirty_date() -> None:
df = pd.DataFrame(
{"Fecha Servicio": ["15/jun/2024"], "Importe": [4500]},
)
result, _log = apply_rules(
df,
{
"Fecha Servicio": ["date_to_ddmmyyyy"],
"Importe": ["cop_mod_split"],
},
cop_mod_fecha_column={"Importe": "Fecha Servicio"},
cop_mod_config={"moderadora_by_year": {"2024": [4500, 18200, 47700]}},
)
assert result["Fecha Servicio"].iloc[0] == "15/06/2024"
assert result["Importe_moderadora"].iloc[0] == 4500
def test_apply_rules_cop_mod_year_not_in_table_goes_copago() -> None:
df = pd.DataFrame(
{"Fecha Servicio": ["01/01/2030"], "Importe": [4500]},
)
result, _log = apply_rules(
df,
{"Importe": ["cop_mod_split"]},
cop_mod_fecha_column={"Importe": "Fecha Servicio"},
cop_mod_config={"moderadora_by_year": {"2024": [4500]}},
)
assert result["Importe_copago"].tolist() == [4500]
assert result["Importe_moderadora"].tolist() == [0]
def test_apply_rules_cop_mod_null_amount_zeros() -> None:
df = pd.DataFrame({"Fecha Servicio": ["01/01/2024"], "Importe": [pd.NA]})
result, _log = apply_rules(
df,
{"Importe": ["cop_mod_split"]},
cop_mod_fecha_column={"Importe": "Fecha Servicio"},
cop_mod_config={"moderadora_by_year": {"2024": [4500]}},
)
assert result["Importe_copago"].tolist() == [0]
assert result["Importe_moderadora"].tolist() == [0]
def test_apply_rules_nota_extract_one_line_sin_autorizacion() -> None:
texto = (
"Las retenciones son a nivel informativo no afecta el total a pagar. "
"Paciente: 1063146603 - VILLA BARRIOS NORMA JUDITH Atención No: 63728; "
"Contrato No. URGENCIAS. Valor en Letras: CIENTO OCHENTA Y SIETE MIL "
"QUINIENTOS TREINTA Y SIETE PESOS Fecha ingreso 19/11/2023 fecha egreso 19/11/2023"
)
df = pd.DataFrame({"notas": [texto]})
result, log = apply_rules(df, {"notas": ["nota_paciente_autorizacion"]})
assert result["notas_nota_paciente_doc"].iloc[0] == "1063146603"
assert pd.isna(result["notas_nota_autorizacion"].iloc[0])
assert any("Extraer datos de notas" in line for line in log)
def test_apply_rules_nota_extract_multiline_con_autorizacion() -> None:
texto = (
"Las retenciones son a nivel informativo no afecta el total a pagar.\n"
"Paciente: 30662603 - LLORENTE HERNANDEZ CARMEN LUCIA Atención No: 69426;\n"
"Autorización No. 227490546; Contrato No. 118. Valor en Letras: CIENTO NOVENTA Y OCHO\n"
"MIL SEISCIENTOS DIEZ PESOS Fecha ingreso 23/01/2024 fecha egreso 23/01/2024"
)
df = pd.DataFrame({"notas": [texto]})
result, _log = apply_rules(df, {"notas": ["nota_paciente_autorizacion"]})
assert result["notas_nota_paciente_doc"].iloc[0] == "30662603"
assert result["notas_nota_autorizacion"].iloc[0] == "227490546"
def test_apply_rules_nota_extract_sin_patrones() -> None:
df = pd.DataFrame({"notas": ["sin datos relevantes aquí"]})
result, _log = apply_rules(df, {"notas": ["nota_paciente_autorizacion"]})
assert pd.isna(result["notas_nota_paciente_doc"].iloc[0])
assert pd.isna(result["notas_nota_autorizacion"].iloc[0])