|
|
|
|
|
""" |
|
|
Interfaz de usuario con Gradio |
|
|
""" |
|
|
|
|
|
import gradio as gr |
|
|
from config import HUGGINGFACE_MODEL |
|
|
|
|
|
|
|
|
class GradioInterface: |
|
|
"""Clase para construir y gestionar la interfaz Gradio.""" |
|
|
|
|
|
def __init__(self, batch_processor, result_navigator): |
|
|
""" |
|
|
Inicializa la interfaz. |
|
|
|
|
|
Args: |
|
|
batch_processor: Instancia de BatchProcessor |
|
|
result_navigator: Clase ResultNavigator (no instancia) |
|
|
""" |
|
|
self.batch_processor = batch_processor |
|
|
self.navigator = result_navigator |
|
|
|
|
|
def update_dataframe_with_edits(self, master_df, current_index, edited_table): |
|
|
""" |
|
|
Actualiza el DataFrame maestro con las ediciones del usuario. |
|
|
|
|
|
Args: |
|
|
master_df: DataFrame maestro |
|
|
current_index: Índice actual |
|
|
edited_table: Tabla editada |
|
|
|
|
|
Returns: |
|
|
pd.DataFrame: DataFrame actualizado |
|
|
""" |
|
|
if master_df is None: |
|
|
return master_df |
|
|
|
|
|
updated_df = self.navigator.update_df_from_table(master_df, current_index, edited_table) |
|
|
return updated_df |
|
|
|
|
|
def verify_and_generate_excel(self, master_df): |
|
|
""" |
|
|
Verifica el DataFrame y genera el Excel. |
|
|
|
|
|
Args: |
|
|
master_df: DataFrame maestro |
|
|
|
|
|
Returns: |
|
|
tuple: (filepath, mensaje) |
|
|
""" |
|
|
from datetime import datetime |
|
|
import os |
|
|
|
|
|
if master_df is None or master_df.empty: |
|
|
return None, "❌ No hay datos para exportar" |
|
|
|
|
|
|
|
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") |
|
|
filename = f"facturas_lote_{timestamp}.xlsx" |
|
|
filepath = os.path.join(os.getcwd(), filename) |
|
|
|
|
|
master_df.to_excel(filepath, index=False, engine='openpyxl') |
|
|
|
|
|
mensaje = f"✓ Excel generado: {filename} ({len(master_df)} facturas)" |
|
|
return filepath, mensaje |
|
|
|
|
|
def enable_buttons(self, master_df): |
|
|
"""Habilita botones si hay DataFrame.""" |
|
|
has_data = master_df is not None and not master_df.empty |
|
|
return ( |
|
|
gr.update(interactive=has_data), |
|
|
gr.update(interactive=has_data), |
|
|
gr.update(interactive=has_data), |
|
|
gr.update(interactive=has_data) |
|
|
) |
|
|
|
|
|
def build_interface(self): |
|
|
"""Construye la interfaz Gradio.""" |
|
|
with gr.Blocks(title="NER de Facturas Argentinas por Lote") as demo: |
|
|
gr.Markdown( |
|
|
f""" |
|
|
# 🇦🇷 Extracción de Datos de Facturas Argentinas |
|
|
Carga las facutas para su procesamiento por Lote |
|
|
Se utiliza **LayoutLMv3** (`{HUGGINGFACE_MODEL}`) y **DocTR** forzando la **ejecución en CPU**. |
|
|
""" |
|
|
) |
|
|
|
|
|
|
|
|
master_dataframe_state = gr.State(value=None) |
|
|
current_index_state = gr.State(value=0) |
|
|
|
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(scale=1): |
|
|
file_input = gr.Files( |
|
|
file_count="multiple", |
|
|
type="filepath", |
|
|
label="📂 Cargar las Facturas", |
|
|
interactive=True |
|
|
) |
|
|
|
|
|
with gr.Column(scale=1): |
|
|
with gr.Row(): |
|
|
process_button = gr.Button( |
|
|
"🚀 Procesar Lote de Facturas", |
|
|
variant="primary", |
|
|
size="lg", |
|
|
scale=2 |
|
|
) |
|
|
download_button = gr.Button( |
|
|
"📥 Descargar XLSX", |
|
|
variant="secondary", |
|
|
size="lg", |
|
|
scale=1, |
|
|
interactive=False |
|
|
) |
|
|
status_output = gr.Textbox( |
|
|
label="📊 Estado", |
|
|
value="Carga tus facturas y haz clic en 'Procesar'", |
|
|
interactive=False |
|
|
) |
|
|
|
|
|
gr.Markdown("---") |
|
|
|
|
|
excel_file = gr.File(label="📥 Archivo Excel Generado", visible=False) |
|
|
|
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(scale=1): |
|
|
image_output = gr.Image(type="pil", label="🖼️ Factura con Entidades") |
|
|
|
|
|
with gr.Column(scale=1): |
|
|
filename_output = gr.Textbox(label="📄 Nombre de Archivo", interactive=False) |
|
|
|
|
|
with gr.Row(): |
|
|
prev_button = gr.Button("⬅️ Anterior", interactive=False, size="lg") |
|
|
save_button = gr.Button("💾 Guardar", variant="secondary", interactive=False, size="lg") |
|
|
next_button = gr.Button("Siguiente ➡️", interactive=False, size="lg") |
|
|
|
|
|
table_output = gr.Dataframe( |
|
|
headers=["Etiqueta", "Valor"], |
|
|
label="📋 Resultados de NER", |
|
|
interactive=True, |
|
|
col_count=(2, "fixed"), |
|
|
datatype=["str", "str"] |
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
process_button.click( |
|
|
fn=self.batch_processor.process_batch, |
|
|
inputs=[file_input], |
|
|
outputs=[ |
|
|
master_dataframe_state, |
|
|
current_index_state, |
|
|
status_output, |
|
|
image_output, |
|
|
filename_output |
|
|
] |
|
|
).then( |
|
|
fn=lambda df, idx: self.navigator.get_row_as_table(df, idx), |
|
|
inputs=[master_dataframe_state, current_index_state], |
|
|
outputs=[table_output] |
|
|
).then( |
|
|
fn=self.enable_buttons, |
|
|
inputs=[master_dataframe_state], |
|
|
outputs=[prev_button, save_button, next_button, download_button] |
|
|
) |
|
|
|
|
|
|
|
|
save_button.click( |
|
|
fn=self.update_dataframe_with_edits, |
|
|
inputs=[master_dataframe_state, current_index_state, table_output], |
|
|
outputs=[master_dataframe_state] |
|
|
).then( |
|
|
fn=lambda idx: f"✓ Cambios guardados en factura {idx + 1}", |
|
|
inputs=[current_index_state], |
|
|
outputs=[status_output] |
|
|
) |
|
|
|
|
|
|
|
|
table_output.change( |
|
|
fn=self.update_dataframe_with_edits, |
|
|
inputs=[master_dataframe_state, current_index_state, table_output], |
|
|
outputs=[master_dataframe_state] |
|
|
) |
|
|
|
|
|
|
|
|
prev_button.click( |
|
|
fn=lambda df, idx: self.navigator.go_prev(df, idx, self.batch_processor), |
|
|
inputs=[master_dataframe_state, current_index_state], |
|
|
outputs=[current_index_state, image_output, table_output, status_output, filename_output] |
|
|
) |
|
|
|
|
|
|
|
|
next_button.click( |
|
|
fn=lambda df, idx: self.navigator.go_next(df, idx, self.batch_processor), |
|
|
inputs=[master_dataframe_state, current_index_state], |
|
|
outputs=[current_index_state, image_output, table_output, status_output, filename_output] |
|
|
) |
|
|
|
|
|
|
|
|
download_button.click( |
|
|
fn=self.verify_and_generate_excel, |
|
|
inputs=[master_dataframe_state], |
|
|
outputs=[excel_file, status_output] |
|
|
).then( |
|
|
fn=lambda: gr.update(visible=True), |
|
|
outputs=[excel_file] |
|
|
) |
|
|
|
|
|
return demo |