import gradio as gr import pandas as pd from typing import Dict, List, Tuple from openpyxl.styles import PatternFill, Font from openpyxl.utils import get_column_letter from datetime import datetime def create_encoding_dict(training_df: pd.DataFrame) -> Dict[str, List[str]]: """ Crea un diccionario de codificación basado en el DataFrame de entrenamiento. Las respuestas pueden tener múltiples códigos separados por punto y coma. """ encoding_dict = {} for _, row in training_df.iterrows(): respuesta = str(row['B']).strip().lower() codigos = str(row['C']).strip().split(';') codigos = [codigo.strip() for codigo in codigos] encoding_dict[respuesta] = codigos return encoding_dict def find_best_match(respuesta: str, encoding_dict: Dict[str, List[str]]) -> Tuple[List[str], float, str]: """ Encuentra la mejor coincidencia para una respuesta en el diccionario de codificación. Retorna los códigos correspondientes, el score de similitud y la respuesta que hizo match. """ from difflib import SequenceMatcher respuesta = respuesta.strip().lower() if respuesta in encoding_dict: return encoding_dict[respuesta], 1.0, respuesta best_score = 0 best_match = None for known_resp in encoding_dict.keys(): score = SequenceMatcher(None, respuesta, known_resp).ratio() if score > best_score: best_score = score best_match = known_resp if best_score > 0.8: return encoding_dict[best_match], best_score, best_match return [], 0.0, "" def format_excel(writer, df, sheet_name="Resultados"): """ Da formato al archivo Excel de salida """ # Escribimos el DataFrame en el Excel df.to_excel(writer, sheet_name=sheet_name, index=False) # Obtenemos la hoja de trabajo workbook = writer.book worksheet = writer.sheets[sheet_name] # Definimos los estilos header_fill = PatternFill(start_color='366092', end_color='366092', fill_type='solid') header_font = Font(color='FFFFFF', bold=True) # Formato para los encabezados for col in range(len(df.columns)): cell = worksheet.cell(row=1, column=col+1) cell.fill = header_fill cell.font = header_font # Ajustamos el ancho de las columnas for col in range(len(df.columns)): max_length = 0 column = get_column_letter(col + 1) for cell in worksheet[column]: try: if len(str(cell.value)) > max_length: max_length = len(str(cell.value)) except: pass adjusted_width = (max_length + 2) worksheet.column_dimensions[column].width = min(adjusted_width, 50) def process_excel(training_file, new_file): """ Procesa los archivos de Excel y codifica las nuevas respuestas. """ try: # Leemos los archivos training_df = pd.read_excel(training_file.name) new_df = pd.read_excel(new_file.name) # Creamos el diccionario de codificación encoding_dict = create_encoding_dict(training_df) # Preparamos las columnas de resultado results_df = new_df.copy() results_df['Códigos_Asignados'] = '' results_df['Score_Similitud'] = 0.0 results_df['Respuesta_Match'] = '' results_df['Fecha_Proceso'] = datetime.now().strftime("%Y-%m-%d %H:%M:%S") # Procesamos cada respuesta nueva for idx, row in results_df.iterrows(): respuesta = str(row['B']) codigos, score, match = find_best_match(respuesta, encoding_dict) results_df.at[idx, 'Códigos_Asignados'] = '; '.join(codigos) if codigos else 'SIN_MATCH' results_df.at[idx, 'Score_Similitud'] = round(score, 2) results_df.at[idx, 'Respuesta_Match'] = match if match else 'N/A' # Creamos un resumen total_respuestas = len(results_df) respuestas_codificadas = len(results_df[results_df['Códigos_Asignados'] != 'SIN_MATCH']) summary_df = pd.DataFrame({ 'Métrica': ['Total Respuestas', 'Respuestas Codificadas', 'Respuestas Sin Match', 'Porcentaje Codificación'], 'Valor': [ total_respuestas, respuestas_codificadas, total_respuestas - respuestas_codificadas, f"{(respuestas_codificadas/total_respuestas*100):.1f}%" ] }) # Guardamos el resultado en un Excel con múltiples hojas timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") output_path = f"resultados_codificacion_{timestamp}.xlsx" with pd.ExcelWriter(output_path, engine='openpyxl') as writer: # Hoja de resultados detallados format_excel(writer, results_df, "Resultados") # Hoja de resumen format_excel(writer, summary_df, "Resumen") # Hoja con datos de entrenamiento format_excel(writer, training_df, "Datos_Entrenamiento") return output_path except Exception as e: return f"Error: {str(e)}" # Creamos la interfaz de Gradio iface = gr.Interface( fn=process_excel, inputs=[ gr.File(label="Archivo de entrenamiento (Excel)"), gr.File(label="Archivo a codificar (Excel)") ], outputs=gr.File(label="Archivo de resultados"), title="Codificador de Respuestas", description="Sube un archivo Excel de entrenamiento (con códigos conocidos) y un archivo nuevo para codificar las respuestas.", examples=[], cache_examples=False ) # Iniciamos la aplicación if __name__ == "__main__": iface.launch()