# services/utils/data_cleaner.py import logging import locale from dateutil import parser from typing import Optional from datetime import datetime SPANISH_TO_ENGLISH_MONTHS = { 'enero': 'january', 'febrero': 'february', 'marzo': 'march', 'abril': 'april', 'mayo': 'may', 'junio': 'june', 'julio': 'july', 'agosto': 'august', 'septiembre': 'september', 'octubre': 'october', 'noviembre': 'november', 'diciembre': 'december' } def _parse_with_fallback(date_string: str) -> Optional[datetime]: """ Intenta parsear la fecha usando un fallback manual que primero limpia preposiciones comunes en español ("de", "del") y luego traduce los meses. """ # 1. Normalizar a minúsculas para trabajar de forma consistente temp_string = date_string.lower() # 2. Traducir el mes de español a inglés for spa, eng in SPANISH_TO_ENGLISH_MONTHS.items(): if spa in temp_string: temp_string = temp_string.replace(spa, eng) break # Salimos del bucle una vez que encontramos y reemplazamos el mes # 3. Eliminar preposiciones comunes, cuidando los espacios para evitar unir palabras temp_string = temp_string.replace(' de ', ' ') temp_string = temp_string.replace(' del ', ' ') # Después de la limpieza, la cadena debería ser algo como '5 january 2030', que es parseable. try: logging.info(f"Attempting to parse cleaned date string: '{temp_string}'") return parser.parse(temp_string) except (parser.ParserError, ValueError): # Si incluso después de la limpieza falla, no podemos hacer más. logging.warning(f"Fallback parsing failed even for cleaned string: '{temp_string}'") return None def normalize_date(date_string: str) -> Optional[str]: """ Parses a date string from various formats and normalizes it to DD/MM/YYYY. It first tries using Spanish locale, and if it fails, it uses a manual cleaning and translation fallback. """ if not date_string: return None original_locale = locale.getlocale(locale.LC_TIME) parsed_date = None # Estrategia 1: Intentar con el locale español try: try: locale.setlocale(locale.LC_TIME, 'es_ES.UTF-8') except locale.Error: locale.setlocale(locale.LC_TIME, 'Spanish') parsed_date = parser.parse(date_string) except (parser.ParserError, ValueError, locale.Error): logging.warning(f"Could not parse date '{date_string}' using Spanish locale. Attempting robust fallback.") # Estrategia 2: Si el locale falla, usar el fallback robusto parsed_date = _parse_with_fallback(date_string) finally: # Siempre restauramos el locale original locale.setlocale(locale.LC_TIME, original_locale) if parsed_date: # Aquí se asegura el formato DD/MM/AAAA. # '%d' -> día con cero (05), '%m' -> mes con cero (01), '%Y' -> año (2030) return parsed_date.strftime('%d/%m/%Y') else: # Si ambas estrategias fallan, registramos el error final logging.error(f"Failed to parse date '{date_string}' with all available methods.") return None