import os ######################################################## def calcular_puntuacion(nombre_archivo, ruc, tipo, serie_numero): """ Calcula una puntuación de 0 a 4 según cuántos elementos coinciden en el nombre del archivo. serie_numero se descompone en serie y número. """ puntos = 0 nombre_archivo = nombre_archivo.upper() # 1. RUC if ruc and ruc != "N/A" and ruc in nombre_archivo: puntos += 1 # 2. Tipo if tipo and tipo != "99" and tipo in nombre_archivo: puntos += 1 # 3. Serie y 4. Número if "-" in serie_numero: partes = serie_numero.split("-") serie = partes[0].upper() numero = partes[1] if serie in nombre_archivo: puntos += 1 if numero in nombre_archivo: puntos += 1 return puntos ######################################################## def procesar_renombrado_agrupado(email_obj, ruta_base): """ Lógica de agrupación y renombrado de archivos según puntuación y tipo. """ metadatos = email_obj.get('metadatos_adjuntos', []) tipos_permitidos = ["01", "03", "07", "08", "02", "09", "14"] # 1. Identificar "Pivotes" (pivotes para agrupar) # Preferencia: XML Maestros. Fallback: PDFs con metadata válida si no hay XMLs. xml_maestros = [m for m in metadatos if m['extension'] == ".xml" and m['tipo'] in tipos_permitidos] if xml_maestros: pivotes = xml_maestros else: # En correos solo PDF, tratamos a cada PDF con nombre propuesto como un pivote pivotes = [m for m in metadatos if m['extension'] == ".pdf" and m['archivo_propuesto'] is not None] if not pivotes: return {} # Nada que agrupar nuevos_archivos = [] mapeo_renombrado = {} # original -> nuevo agrupacion = {} # archivo_propuesto -> [lista de nombres nuevos] # Inicializar el diccionario de agrupación con los nombres de los pivotes for p in pivotes: agrupacion[p['archivo_propuesto']] = [] # 2. Agrupar cada archivo con su mejor pivote for m in metadatos: nombre_original = m['archivo'] extension = m['extension'].lower() # Si es un pivote, su nombre es directo y ya está inicializado if m in pivotes: nombre_nuevo = f"{m['archivo_propuesto']}{extension}" mapeo_renombrado[nombre_original] = nombre_nuevo agrupacion[m['archivo_propuesto']].append(nombre_nuevo) else: # Buscar el mejor match entre los pivotes que aún acepten este tipo de archivo mejor_pivote = None max_puntos = -1 is_cdr = m['tipo'] == "CDR" or "R-" in nombre_original or "R_" in nombre_original or "CDR" in nombre_original.upper() is_pdf = extension == ".pdf" is_xml = extension == ".xml" and not is_cdr for p in pivotes: p_prop = p['archivo_propuesto'] grupo_actual = agrupacion[p_prop] ya_tiene_pdf = any(f.lower().endswith('.pdf') for f in grupo_actual) ya_tiene_cdr = any(f.startswith('CDR-') for f in grupo_actual) if (is_pdf and ya_tiene_pdf) or (is_cdr and ya_tiene_cdr) or is_xml: continue puntos = calcular_puntuacion( nombre_original, p['ruc'], p['tipo'], p['serie_numero'] ) if puntos > max_puntos: max_puntos = puntos mejor_pivote = p if mejor_pivote and max_puntos > 0: prefix = "CDR-" if is_cdr else "" nombre_nuevo = f"{prefix}{mejor_pivote['archivo_propuesto']}{extension}" mapeo_renombrado[nombre_original] = nombre_nuevo agrupacion[mejor_pivote['archivo_propuesto']].append(nombre_nuevo) else: # Si no hay match, va a OTROS mapeo_renombrado[nombre_original] = nombre_original if "OTROS" not in agrupacion: agrupacion["OTROS"] = [] agrupacion["OTROS"].append(nombre_original) # 3. Ejecutar el renombrado físico y actualizar metadatos for m in metadatos: nombre_original = m['archivo'] nombre_nuevo = mapeo_renombrado.get(nombre_original) if nombre_nuevo and nombre_nuevo != nombre_original: ruta_old = os.path.join(ruta_base, nombre_original) ruta_new = os.path.join(ruta_base, nombre_nuevo) try: if os.path.exists(ruta_old): # Si el destino ya existe, borramos para sobreescribir (o podrías manejarlo de otra forma) if os.path.exists(ruta_new) and ruta_old != ruta_new: os.remove(ruta_new) os.rename(ruta_old, ruta_new) m['archivo'] = nombre_nuevo except Exception as e: print(f" ⚠️ Error renombrando {nombre_original}: {e}") # 4. Actualizar la lista de archivos en el email_obj email_obj['archivos'] = [m['archivo'] for m in metadatos] return agrupacion ######################################################## def mover_archivos_a_resultado(email_obj, ruta_origen, ruta_destino): """ Mueve los archivos procesados de la carpeta temporal a la carpeta final de resultados. Solo mueve archivos que pertenecen a un grupo válido (basado en archivo_propuesto). Excluye los archivos en la categoría 'OTROS'. """ agrupacion = email_obj.get('agrupacion_archivos', {}) if not agrupacion: return if not os.path.exists(ruta_destino): os.makedirs(ruta_destino) print(f" 📁 Carpeta de destino creada: {ruta_destino}") for key, archivos in agrupacion.items(): if key == "OTROS": continue # No mover archivos no identificados for archivo in archivos: ruta_old = os.path.join(ruta_origen, archivo) ruta_new = os.path.join(ruta_destino, archivo) try: if os.path.exists(ruta_old): if os.path.exists(ruta_new) and ruta_old != ruta_new: os.remove(ruta_new) os.rename(ruta_old, ruta_new) except Exception as e: print(f" ⚠️ Error trasladando {archivo} a resultado: {e}") ######################################################## def limpiar_carpeta_descarga(ruta): """ Elimina todos los archivos dentro de la carpeta de descarga para dejarla limpia. """ if not os.path.exists(ruta): return print(f" 🧹 Limpiando carpeta de descarga: {ruta}") for archivo in os.listdir(ruta): ruta_archivo = os.path.join(ruta, archivo) try: if os.path.isfile(ruta_archivo): os.remove(ruta_archivo) elif os.path.isdir(ruta_archivo): import shutil shutil.rmtree(ruta_archivo) except Exception as e: print(f" ⚠️ No se pudo eliminar {archivo}: {e}") ########################################################