"""Aplicación de reglas seleccionadas sobre un DataFrame.""" from __future__ import annotations import logging from typing import Any, Optional import pandas as pd from .cop_mod import ( _apply_cop_mod_bruto_neto, _apply_cop_mod_split, merge_moderadora_config, ) from .documents import _apply_doc_id_split_columns from .notas import _apply_nota_extract_columns from .rules import get_rule_fn, get_rule_name logger = logging.getLogger(__name__) def apply_rules( df: pd.DataFrame, rules_per_column: dict[str, list[str]], *, cop_mod_fecha_column: Optional[dict[str, str]] = None, cop_mod_config: Optional[dict[str, Any]] = None, cop_mod_bruto_column: Optional[dict[str, str]] = None, cop_mod_neto_column: Optional[dict[str, str]] = None, ) -> tuple[pd.DataFrame, list[str]]: """Aplica las reglas seleccionadas a cada columna. `rules_per_column` es un dict de `columna -> lista de IDs de reglas en el orden a aplicar`. Las reglas `cop_mod_split` y `cop_mod_bruto_neto` se difieren a una segunda pasada tras el resto. `cop_mod_fecha_column`: mapa columna ancla -> Fecha Servicio (para split). `cop_mod_bruto_column` / `cop_mod_neto_column`: mapas ancla -> bruto / neto. `cop_mod_config`: opcional; puede incluir ``moderadora_by_year``. """ result = df.copy() log: list[str] = [] moderadora_years = merge_moderadora_config(cop_mod_config) deferred = {"cop_mod_split", "cop_mod_bruto_neto"} def _run_rules_for_column(col: str, rule_ids: list[str]) -> None: if not rule_ids: return if col not in result.columns: log.append(f"[WARN] Columna no encontrada: {col}") return split_has_homologation = ( "doc_id_split_tipo_numero" in rule_ids and "doc_type_homologate" in rule_ids ) for rule_id in rule_ids: if rule_id == "doc_id_split_tipo_numero": try: _apply_doc_id_split_columns( result, col, log, homologate_type=split_has_homologation ) except Exception as exc: # noqa: BLE001 logger.exception("Error aplicando regla %s a %s", rule_id, col) log.append(f"[ERROR] '{col}' regla '{rule_id}': {exc}") continue if rule_id == "nota_paciente_autorizacion": try: _apply_nota_extract_columns(result, col, log) except Exception as exc: # noqa: BLE001 logger.exception("Error aplicando regla %s a %s", rule_id, col) log.append(f"[ERROR] '{col}' regla '{rule_id}': {exc}") continue if rule_id == "doc_type_homologate" and split_has_homologation: continue fn = get_rule_fn(rule_id) if fn is None: log.append(f"[WARN] Regla desconocida '{rule_id}' en columna '{col}'") continue try: result[col] = fn(result[col]) log.append(f"[OK] '{col}' <- {get_rule_name(rule_id)}") except Exception as exc: # noqa: BLE001 logger.exception("Error aplicando regla %s a %s", rule_id, col) log.append(f"[ERROR] '{col}' regla '{rule_id}': {exc}") # Fase 1: todas las reglas excepto COP/MOD diferidas for col, rule_ids in rules_per_column.items(): filtered = [r for r in rule_ids if r not in deferred] _run_rules_for_column(col, filtered) # Fase 2a: Separa COP/MOD (cuotas por año) fecha_map = cop_mod_fecha_column or {} for col, rule_ids in rules_per_column.items(): if "cop_mod_split" not in rule_ids: continue fecha_col = fecha_map.get(col) if not fecha_col: log.append( f"[WARN] COP/MOD en '{col}': indique la columna «Fecha Servicio» en la tarjeta; se omite." ) continue try: _apply_cop_mod_split( result, col, fecha_col, log, moderadora_by_year=moderadora_years, ) except Exception as exc: # noqa: BLE001 logger.exception("Error aplicando COP/MOD a %s", col) log.append(f"[ERROR] '{col}' regla 'cop_mod_split': {exc}") # Fase 2b: COP/MOD = bruto − neto bruto_map = cop_mod_bruto_column or {} neto_map = cop_mod_neto_column or {} for col, rule_ids in rules_per_column.items(): if "cop_mod_bruto_neto" not in rule_ids: continue bruto_col = (bruto_map.get(col) or col).strip() neto_col = (neto_map.get(col) or "").strip() if not neto_col: log.append( f"[WARN] COP/MOD (bruto−neto) en '{col}': indique la columna «Valor neto»; se omite." ) continue try: _apply_cop_mod_bruto_neto(result, bruto_col, neto_col, log) except Exception as exc: # noqa: BLE001 logger.exception("Error aplicando COP/MOD bruto−neto a %s", col) log.append(f"[ERROR] '{col}' regla 'cop_mod_bruto_neto': {exc}") return result, log