64 lines
3.0 KiB
Python
64 lines
3.0 KiB
Python
# services/utils/data_cleaner.py
|
|
import logging
|
|
import locale
|
|
import re
|
|
from dateutil import parser
|
|
from typing import Optional, Dict
|
|
from datetime import datetime
|
|
|
|
# --- ESTA SECCIÓN NO REQUIERE CAMBIOS ---
|
|
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]:
|
|
temp_string = date_string.lower().replace(' de ', ' ').replace(' del ', ' ')
|
|
for spa, eng in SPANISH_TO_ENGLISH_MONTHS.items():
|
|
if spa in temp_string: temp_string = temp_string.replace(spa, eng); break
|
|
try: return parser.parse(temp_string)
|
|
except (parser.ParserError, ValueError): return None
|
|
def normalize_date(date_string: str) -> Optional[str]:
|
|
if not date_string: return None
|
|
original_locale = locale.getlocale(locale.LC_TIME)
|
|
parsed_date = None
|
|
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):
|
|
parsed_date = _parse_with_fallback(date_string)
|
|
finally: locale.setlocale(locale.LC_TIME, original_locale)
|
|
return parsed_date.strftime('%d/%m/%Y') if parsed_date else None
|
|
# --- FIN DE LA SECCIÓN SIN CAMBIOS ---
|
|
|
|
def clean_numeric_value(text: str) -> str:
|
|
"""Función pública para limpiar y normalizar un string numérico."""
|
|
if not text: return "0.00"
|
|
cleaned = text.strip().replace('.', '').replace(',', '.')
|
|
try: return f"{float(cleaned):.2f}"
|
|
except (ValueError, TypeError):
|
|
logging.warning(f"Could not convert '{text}' to a numeric value. Defaulting to 0.00.")
|
|
return "0.00"
|
|
|
|
def parse_total_and_tax(text: str) -> Dict[str, Optional[str]]:
|
|
"""
|
|
Versión final y robusta. Parsea un string que contiene el total y el IVA.
|
|
"""
|
|
logging.info(f"Analizando línea de importe: '{text}'")
|
|
result = {'total_amount': None, 'total_tax_amount': None}
|
|
|
|
normalized_text = " ".join(text.split())
|
|
|
|
# Regex para el total: busca el primer número después de la palabra "Total"
|
|
total_match = re.search(r'total\s*([\d.,]+)', normalized_text, re.IGNORECASE)
|
|
if total_match:
|
|
result['total_amount'] = clean_numeric_value(total_match.group(1))
|
|
|
|
# Regex de alta precisión para el IVA
|
|
tax_match = re.search(r'\(.*?(?:incluye|incluido)\s+([\d.,]+)€?\s*IVA.*?\)', normalized_text, re.IGNORECASE)
|
|
if tax_match:
|
|
result['total_tax_amount'] = clean_numeric_value(tax_match.group(1))
|
|
|
|
if result['total_amount'] and result['total_tax_amount']:
|
|
logging.info(f"¡ÉXITO! Total: {result['total_amount']}, IVA: {result['total_tax_amount']}")
|
|
else:
|
|
logging.warning(f"Fallo en el parsing de importes para la línea: '{normalized_text}'")
|
|
|
|
return result |