refactor: Introduce accumulate_usage function for token aggregation, enhance alias map expansion in BatchProcessor, and streamline error handling in PreclassificationService. Update PDF processing utilities for improved image conversion and error logging.
This commit is contained in:
+6
-25
@@ -18,7 +18,7 @@ from application.preclassification import PreclassificationService
|
||||
from application.strategies import OllamaExtractor
|
||||
from application.database import DatabaseManager
|
||||
from application.config import settings
|
||||
from application.costs import compute_cost_from_usage, MODEL_PRICING, sum_costs
|
||||
from application.costs import MODEL_PRICING
|
||||
import pandas as pd
|
||||
|
||||
|
||||
@@ -168,8 +168,7 @@ class AppController:
|
||||
selected = fd.askdirectory()
|
||||
if not selected:
|
||||
return
|
||||
self.view.root_entry.delete(0, 'end')
|
||||
self.view.root_entry.insert(0, selected)
|
||||
self.view.set_root_path(selected)
|
||||
|
||||
# Detectar si el primer documento a procesar es TIFF para esta carpeta
|
||||
try:
|
||||
@@ -195,26 +194,15 @@ class AppController:
|
||||
selected = fd.askdirectory()
|
||||
if not selected:
|
||||
return
|
||||
try:
|
||||
self.view.preclass_root_entry.delete(0, 'end')
|
||||
self.view.preclass_root_entry.insert(0, selected)
|
||||
except Exception:
|
||||
# fallback si cambia el nombre del widget
|
||||
try:
|
||||
setter = getattr(self.view, "set_preclass_root_path", None)
|
||||
if callable(setter):
|
||||
setter(selected)
|
||||
except Exception:
|
||||
pass
|
||||
self.view.set_preclass_root_path(selected)
|
||||
|
||||
def on_run_preclassification(self):
|
||||
"""Ejecuta la preclasificación en un hilo separado."""
|
||||
# 1. Leer inputs de la UI
|
||||
root = self.view.get_preclass_root_path()
|
||||
mode = getattr(self.view, "get_preclass_mode", lambda: "radicados")()
|
||||
patterns = self.view.get_preclass_patterns()
|
||||
target_info = self.view.get_preclass_target_info()
|
||||
page_range = self.view.get_preclass_range() # (enabled, start, end)
|
||||
page_range = self.view.get_preclass_range()
|
||||
mode = self.view.get_preclass_mode()
|
||||
|
||||
# Validaciones básicas
|
||||
@@ -359,15 +347,9 @@ class AppController:
|
||||
# --- Pestaña Resultados ---
|
||||
def on_res_select(self, event):
|
||||
"""Muestra detalles del resultado seleccionado."""
|
||||
selection = self.view.tree_results.selection()
|
||||
if not selection:
|
||||
return
|
||||
|
||||
# values: (ID, Run, Fecha, Radicado, Estado, Archivo)
|
||||
item_vals = self.view.tree_results.item(selection[0], "values")
|
||||
item_vals = self.view.get_selected_result_values()
|
||||
if not item_vals:
|
||||
return
|
||||
|
||||
try:
|
||||
res_id = int(item_vals[0])
|
||||
matches = self.db_manager.get_matches_for_result(res_id)
|
||||
@@ -464,8 +446,7 @@ class AppController:
|
||||
"""Abre un diálogo de guardado y establece la ruta del CSV de salida."""
|
||||
selected = fd.asksaveasfilename(defaultextension=".csv", filetypes=[("CSV", "*.csv"), ("Todos", "*.*")])
|
||||
if selected:
|
||||
self.view.csv_entry.delete(0, 'end')
|
||||
self.view.csv_entry.insert(0, selected)
|
||||
self.view.set_csv_path(selected)
|
||||
|
||||
def on_process(self):
|
||||
"""Inicia el flujo de procesamiento por radicado (solo facturas) en un hilo de fondo.
|
||||
|
||||
+15
-1
@@ -25,6 +25,8 @@ class MainProcessingTab:
|
||||
self._create_config_section()
|
||||
self._create_process_button()
|
||||
self._create_output_section()
|
||||
# Conectar el trace después de que todos los widgets existen
|
||||
self.mode_var.trace_add("write", lambda *_: self.update_ui_for_mode())
|
||||
|
||||
def _setup_layout(self):
|
||||
"""Configura el layout base de la pestaña."""
|
||||
@@ -297,10 +299,22 @@ class MainProcessingTab:
|
||||
def get_root_path(self) -> str:
|
||||
"""Devuelve la ruta raíz seleccionada."""
|
||||
return self.root_entry.get().strip()
|
||||
|
||||
|
||||
def set_root_path(self, path: str) -> None:
|
||||
"""Establece la ruta raíz."""
|
||||
self.root_entry.configure(state="normal")
|
||||
self.root_entry.delete(0, "end")
|
||||
self.root_entry.insert(0, path)
|
||||
|
||||
def get_csv_path(self) -> str:
|
||||
"""Devuelve la ruta del CSV."""
|
||||
return self.csv_entry.get().strip()
|
||||
|
||||
def set_csv_path(self, path: str) -> None:
|
||||
"""Establece la ruta del CSV."""
|
||||
self.csv_entry.configure(state="normal")
|
||||
self.csv_entry.delete(0, "end")
|
||||
self.csv_entry.insert(0, path)
|
||||
|
||||
def get_process_mode(self) -> str:
|
||||
"""Devuelve el modo de procesamiento: 'radicados' o 'general'."""
|
||||
|
||||
@@ -3,8 +3,11 @@ import customtkinter as ctk
|
||||
import tkinter.ttk as ttk
|
||||
from gui.styles import AppStyles
|
||||
|
||||
# matplotlib es opcional
|
||||
# matplotlib y numpy son opcionales
|
||||
try:
|
||||
import matplotlib
|
||||
matplotlib.use("Agg") # backend no interactivo para render en canvas Tk
|
||||
import numpy as np
|
||||
from matplotlib.figure import Figure
|
||||
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
||||
HAS_MPL = True
|
||||
@@ -226,7 +229,6 @@ class MetricsTab:
|
||||
extract_vals = [input_extract, cached_extract, output_extract]
|
||||
norm_vals = [input_norm, cached_norm, output_norm]
|
||||
|
||||
import numpy as np
|
||||
x = np.arange(len(labels))
|
||||
width = 0.35
|
||||
|
||||
|
||||
@@ -388,7 +388,13 @@ class PreclassificationTab:
|
||||
def get_root_path(self) -> str:
|
||||
"""Devuelve la ruta raíz de preclasificación."""
|
||||
return self.preclass_root_entry.get().strip()
|
||||
|
||||
|
||||
def set_root_path(self, path: str) -> None:
|
||||
"""Establece la ruta raíz de preclasificación."""
|
||||
self.preclass_root_entry.configure(state="normal")
|
||||
self.preclass_root_entry.delete(0, "end")
|
||||
self.preclass_root_entry.insert(0, path)
|
||||
|
||||
def get_mode(self) -> str:
|
||||
"""Devuelve el modo de preclasificación: 'radicados' o 'general'."""
|
||||
try:
|
||||
|
||||
@@ -202,6 +202,13 @@ class ResultsTab:
|
||||
def set_on_select_result(self, callback):
|
||||
"""Vincula el callback al evento de selección de fila."""
|
||||
self.tree_results.bind("<<TreeviewSelect>>", callback)
|
||||
|
||||
def get_selected_result_values(self) -> tuple | None:
|
||||
"""Devuelve los valores de la fila seleccionada o None si no hay selección."""
|
||||
selection = self.tree_results.selection()
|
||||
if not selection:
|
||||
return None
|
||||
return self.tree_results.item(selection[0], "values") or None
|
||||
|
||||
def update_matches_table(self, rows):
|
||||
"""Actualiza la tabla de matches con los datos proporcionados."""
|
||||
|
||||
+13
-13
@@ -3,19 +3,11 @@
|
||||
Expone métodos para enlazar callbacks, leer entradas y escribir salida en un
|
||||
área de texto, además de habilitar/deshabilitar controles durante el proceso.
|
||||
"""
|
||||
import os # Necesario para basename en update_res_table
|
||||
import os
|
||||
import customtkinter as ctk
|
||||
from gui.styles import AppStyles
|
||||
from gui.tabs import SettingsTab, ResultsTab, MainProcessingTab, PreclassificationTab, MetricsTab
|
||||
|
||||
# matplotlib es opcional
|
||||
try:
|
||||
import matplotlib
|
||||
matplotlib.use("Agg") # backend no interactivo para render en canvas
|
||||
HAS_MPL = True
|
||||
except Exception:
|
||||
HAS_MPL = False
|
||||
|
||||
|
||||
class InvoiceAppView(ctk.CTk):
|
||||
"""Ventana principal con pestañas de Procesar y Configuración."""
|
||||
@@ -160,13 +152,21 @@ class InvoiceAppView(ctk.CTk):
|
||||
def append_preclass_output_safe(self, text: str) -> None:
|
||||
self.after(0, lambda: self.preclass_tab.append_output_safe(text))
|
||||
|
||||
def _update_ui_for_mode(self):
|
||||
"""Actualiza la visibilidad de componentes según el modo seleccionado."""
|
||||
self.main_tab.update_ui_for_mode()
|
||||
|
||||
def get_root_path(self) -> str:
|
||||
return self.main_tab.get_root_path()
|
||||
|
||||
def set_root_path(self, path: str) -> None:
|
||||
self.main_tab.set_root_path(path)
|
||||
|
||||
def set_csv_path(self, path: str) -> None:
|
||||
self.main_tab.set_csv_path(path)
|
||||
|
||||
def set_preclass_root_path(self, path: str) -> None:
|
||||
self.preclass_tab.set_root_path(path)
|
||||
|
||||
def get_selected_result_values(self) -> tuple | None:
|
||||
return self.results_tab.get_selected_result_values()
|
||||
|
||||
def get_process_mode(self) -> str:
|
||||
"""Devuelve el modo de procesamiento: 'radicados' o 'general'."""
|
||||
return self.main_tab.get_process_mode()
|
||||
|
||||
Reference in New Issue
Block a user