400 lines
14 KiB
Python
400 lines
14 KiB
Python
"""Vista principal de la aplicación GUI con `customtkinter`.
|
|
|
|
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
|
|
import customtkinter as ctk
|
|
from gui.styles import AppStyles
|
|
from gui.tabs import SettingsTab, ResultsTab, MainProcessingTab, PreclassificationTab, MetricsTab, PostProcessTab, CleaningTab
|
|
|
|
|
|
class InvoiceAppView(ctk.CTk):
|
|
"""Ventana principal con pestañas de Procesar y Configuración."""
|
|
def __init__(self):
|
|
"""Construye la ventana principal y sus pestañas/controles."""
|
|
super().__init__()
|
|
self.title("IPSExtractor - Procesador de Documentos")
|
|
self.geometry("1100x800")
|
|
|
|
# Configurar tema
|
|
AppStyles.configure_theme()
|
|
|
|
# Obtener estilos
|
|
self.fonts = AppStyles.get_fonts()
|
|
self.colors = AppStyles.get_colors()
|
|
|
|
# Retrocompatibilidad: usar los nombres antiguos
|
|
self.font_title = self.fonts["title"]
|
|
self.font_section = self.fonts["section"]
|
|
self.font_ui = self.fonts["ui"]
|
|
self.font_mono = self.fonts["mono"]
|
|
|
|
# Layout principal
|
|
self.grid_columnconfigure(0, weight=1)
|
|
self.grid_rowconfigure(0, weight=1)
|
|
|
|
# TabView principal con estilo
|
|
self.tabs = ctk.CTkTabview(self, width=1000, height=700)
|
|
self.tabs.grid(row=0, column=0, sticky="nsew", padx=20, pady=20)
|
|
|
|
self.tab_main = self.tabs.add("Procesar Documentos")
|
|
self.tab_preclass = self.tabs.add("Preclasificación")
|
|
self.tab_results = self.tabs.add("Resultados")
|
|
self.tab_settings = self.tabs.add("Configuración")
|
|
self.tab_usage = self.tabs.add("Dashboard & Métricas")
|
|
self.tab_postprocess = self.tabs.add("Post-Procesamiento")
|
|
self.tab_cleaning = self.tabs.add("Proceso de Limpieza")
|
|
|
|
# =========================================================================
|
|
# --- TAB: PROCESAR DOCUMENTOS ---
|
|
# =========================================================================
|
|
self.main_tab = MainProcessingTab(self.tab_main)
|
|
|
|
# =========================================================================
|
|
# --- TAB: RESULTADOS ---
|
|
# =========================================================================
|
|
self.results_tab = ResultsTab(self.tab_results)
|
|
|
|
|
|
# =========================================================================
|
|
# --- TAB: CONFIGURACIÓN ---
|
|
# =========================================================================
|
|
self.settings_tab = SettingsTab(self.tab_settings)
|
|
|
|
# =========================================================================
|
|
# --- TAB: PRECLASIFICACIÓN ---
|
|
# =========================================================================
|
|
self.preclass_tab = PreclassificationTab(self.tab_preclass)
|
|
|
|
|
|
# =========================================================================
|
|
# --- TAB: DASHBOARD & MÉTRICAS ---
|
|
# =========================================================================
|
|
self.metrics_tab = MetricsTab(self.tab_usage)
|
|
|
|
# =========================================================================
|
|
# --- TAB: POST-PROCESAMIENTO ---
|
|
# =========================================================================
|
|
self.postprocess_tab = PostProcessTab(self.tab_postprocess)
|
|
|
|
# =========================================================================
|
|
# --- TAB: PROCESO DE LIMPIEZA ---
|
|
# =========================================================================
|
|
self.cleaning_tab = CleaningTab(self.tab_cleaning)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# MÉTODOS PÚBLICOS (API COMPATIBLE)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def set_on_select_root(self, callback):
|
|
self.main_tab.set_on_select_root(callback)
|
|
|
|
def set_on_select_preclass_root(self, callback):
|
|
self.preclass_tab.set_on_select_root(callback)
|
|
|
|
def set_on_process(self, callback):
|
|
self.main_tab.set_on_process(callback)
|
|
|
|
def set_on_select_csv(self, callback):
|
|
self.main_tab.set_on_select_csv(callback)
|
|
|
|
def set_on_save_settings(self, callback):
|
|
self.settings_tab.set_on_save(callback)
|
|
|
|
def get_selected_model(self) -> str:
|
|
return self.settings_tab.get_selected_model()
|
|
|
|
def set_selected_model(self, model: str) -> None:
|
|
self.settings_tab.set_selected_model(model)
|
|
|
|
def get_concurrency_limit(self) -> int:
|
|
return self.settings_tab.get_concurrency_limit()
|
|
|
|
def set_concurrency_limit(self, limit: int) -> None:
|
|
self.settings_tab.set_concurrency_limit(limit)
|
|
|
|
def get_allowed_document_types(self) -> list[str]:
|
|
return self.settings_tab.get_allowed_document_types()
|
|
|
|
def set_allowed_document_types(self, values: list[str]) -> None:
|
|
self.settings_tab.set_allowed_document_types(values)
|
|
|
|
def set_on_run_preclassification(self, callback):
|
|
self.preclass_tab.set_on_run(callback)
|
|
|
|
def set_on_export_excel(self, callback):
|
|
self.preclass_tab.set_on_export_excel(callback)
|
|
|
|
def set_on_organize_files(self, callback):
|
|
self.preclass_tab.set_on_organize_files(callback)
|
|
|
|
def enable_preclass_actions(self, enabled: bool):
|
|
self.preclass_tab.enable_actions(enabled)
|
|
|
|
# --- Métodos Resultados ---
|
|
def set_on_res_search(self, callback):
|
|
self.results_tab.set_on_search(callback)
|
|
|
|
def set_on_res_export(self, callback):
|
|
self.results_tab.set_on_export(callback)
|
|
|
|
def set_on_res_refresh(self, callback):
|
|
self.results_tab.set_on_refresh(callback)
|
|
|
|
def set_on_res_organize(self, callback):
|
|
self.results_tab.set_on_organize(callback)
|
|
|
|
def set_on_res_select(self, callback):
|
|
self.results_tab.set_on_select_result(callback)
|
|
|
|
def update_res_matches_table(self, rows):
|
|
self.results_tab.update_matches_table(rows)
|
|
|
|
def update_res_runs_combo(self, runs: list):
|
|
self.results_tab.update_runs_combo(runs)
|
|
|
|
def set_res_run(self, run_id: int | str):
|
|
self.results_tab.set_run_id(run_id)
|
|
|
|
def get_res_filters(self):
|
|
return self.results_tab.get_filters()
|
|
|
|
def update_res_table(self, rows):
|
|
self.results_tab.update_results_table(rows)
|
|
|
|
def append_preclass_output(self, text: str) -> None:
|
|
self.preclass_tab.append_output(text)
|
|
|
|
def append_preclass_output_safe(self, text: str) -> None:
|
|
self.after(0, lambda: self.preclass_tab.append_output_safe(text))
|
|
|
|
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()
|
|
|
|
def set_process_mode(self, mode: str) -> None:
|
|
self.main_tab.set_process_mode(mode)
|
|
|
|
def get_preclass_root_path(self) -> str:
|
|
return self.preclass_tab.get_root_path()
|
|
|
|
def get_preclass_mode(self) -> str:
|
|
"""Devuelve el modo de preclasificación: 'radicados' o 'general'."""
|
|
return self.preclass_tab.get_mode()
|
|
|
|
def get_preclass_range(self) -> tuple[bool, int, int]:
|
|
"""Devuelve (habilitado, desde, hasta) para preclasificación."""
|
|
return self.preclass_tab.get_range()
|
|
|
|
def get_preclass_target_info(self) -> dict:
|
|
"""Devuelve la configuración del archivo objetivo."""
|
|
return self.preclass_tab.get_target_info()
|
|
|
|
def get_keywords(self) -> str:
|
|
return self.main_tab.get_keywords()
|
|
|
|
def get_fields(self) -> str:
|
|
return self.main_tab.get_fields()
|
|
|
|
def get_csv_path(self) -> str:
|
|
return self.main_tab.get_csv_path()
|
|
|
|
def get_retry_empty_enabled(self) -> bool:
|
|
return self.main_tab.get_retry_empty_enabled()
|
|
|
|
def set_retry_empty_enabled(self, enabled: bool) -> None:
|
|
self.main_tab.set_retry_empty_enabled(enabled)
|
|
|
|
def get_radicado_filter_enabled(self) -> bool:
|
|
return self.main_tab.get_radicado_filter_enabled()
|
|
|
|
def get_radicado_filter_list(self) -> list[str]:
|
|
return self.main_tab.get_radicado_filter_list()
|
|
|
|
def show_tiff_range_controls(self, show: bool) -> None:
|
|
self.main_tab.show_tiff_range_controls(show)
|
|
|
|
def get_tiff_start_page(self) -> int:
|
|
return self.main_tab.get_tiff_start_page()
|
|
|
|
def get_tiff_end_page(self) -> int:
|
|
return self.main_tab.get_tiff_end_page()
|
|
|
|
def append_output(self, text: str) -> None:
|
|
self.main_tab.append_output(text)
|
|
|
|
def append_output_safe(self, text: str) -> None:
|
|
self.after(0, lambda: self.main_tab.append_output_safe(text))
|
|
|
|
def set_processing(self, processing: bool) -> None:
|
|
self.main_tab.enable_controls(not processing)
|
|
|
|
|
|
# --- TAB PRECLASIFICACIÓN ---
|
|
try:
|
|
# Actualizar controles de la pestaña de preclasificación
|
|
self.preclass_tab.enable_controls(not processing)
|
|
|
|
# Actualizar controles de la pestaña de configuración
|
|
self.settings_tab.enable_controls(not processing)
|
|
except Exception:
|
|
pass
|
|
|
|
# --- TAB USO ---
|
|
|
|
def append_usage(self, text: str) -> None:
|
|
self.metrics_tab.append_usage(text)
|
|
|
|
def append_usage_safe(self, text: str) -> None:
|
|
self.after(0, lambda: self.metrics_tab.append_usage_safe(text))
|
|
|
|
def clear_usage(self) -> None:
|
|
self.metrics_tab.clear()
|
|
|
|
def set_usage_totals(self, **kwargs):
|
|
self.metrics_tab.set_usage_totals(**kwargs)
|
|
|
|
def update_kpis(self, overall: dict, extract_phase: dict, normalize_phase: dict) -> None:
|
|
self.metrics_tab.update_kpis(overall, extract_phase, normalize_phase)
|
|
|
|
def update_doc_kpi(self, processed: int, avg_cost: float) -> None:
|
|
self.metrics_tab.update_doc_kpi(processed, avg_cost)
|
|
|
|
def update_cost_line(self, doc_index: int, total_cost_usd: float) -> None:
|
|
self.metrics_tab.update_cost_line(doc_index, total_cost_usd)
|
|
|
|
def update_token_bars(self, input_extract: int, cached_extract: int, output_extract: int,
|
|
input_norm: int, cached_norm: int, output_norm: int,
|
|
input_pp: int = 0, output_pp: int = 0) -> None:
|
|
self.metrics_tab.update_token_bars(input_extract, cached_extract, output_extract,
|
|
input_norm, cached_norm, output_norm,
|
|
input_pp, output_pp)
|
|
|
|
def update_comparison_table(self, comparisons: list[dict]) -> None:
|
|
self.metrics_tab.update_comparison_table(comparisons)
|
|
|
|
def append_usage_row(self, phase: str, radicado: str, usage: dict | None, cost: dict | None) -> None:
|
|
self.metrics_tab.append_usage_row(phase, radicado, usage, cost)
|
|
|
|
def update_pp_kpis(self, total_usd: float, total_calls: int, total_retries: int,
|
|
input_tokens: int, output_tokens: int, last_description: str) -> None:
|
|
self.metrics_tab.update_pp_kpis(total_usd, total_calls, total_retries,
|
|
input_tokens, output_tokens, last_description)
|
|
|
|
# --- HELPERS INTERNOS ---
|
|
|
|
def get_preclass_patterns(self) -> list[dict]:
|
|
"""Devuelve la lista de patrones de preclasificación definida en la pestaña."""
|
|
return self.preclass_tab.get_patterns()
|
|
|
|
# --- TAB POST-PROCESAMIENTO ---
|
|
|
|
def set_on_postprocess_select_csv(self, callback):
|
|
self.postprocess_tab.set_on_select_csv(callback)
|
|
|
|
def set_on_postprocess_run(self, callback):
|
|
self.postprocess_tab.set_on_run(callback)
|
|
|
|
def set_on_postprocess_save(self, callback):
|
|
self.postprocess_tab.set_on_save(callback)
|
|
|
|
def get_postprocess_instructions(self) -> str:
|
|
return self.postprocess_tab.get_instructions()
|
|
|
|
def set_postprocess_csv_path(self, path: str):
|
|
self.postprocess_tab.set_csv_path(path)
|
|
|
|
def set_postprocess_preview(self, text: str):
|
|
self.postprocess_tab.set_preview(text)
|
|
|
|
def set_postprocess_code(self, text: str):
|
|
self.postprocess_tab.set_code(text)
|
|
|
|
def append_postprocess_log(self, text: str):
|
|
self.postprocess_tab.append_log(text)
|
|
|
|
def append_postprocess_log_safe(self, text: str):
|
|
self.after(0, lambda: self.postprocess_tab.append_log(text))
|
|
|
|
def clear_postprocess_log(self):
|
|
self.postprocess_tab.clear_log()
|
|
|
|
def set_postprocess_result(self, text: str):
|
|
self.postprocess_tab.set_result(text)
|
|
|
|
def set_postprocess_status(self, text: str):
|
|
self.postprocess_tab.set_status(text)
|
|
|
|
def enable_postprocess_save(self, enabled: bool):
|
|
self.postprocess_tab.enable_save(enabled)
|
|
|
|
def enable_postprocess_controls(self, enabled: bool):
|
|
self.postprocess_tab.enable_controls(enabled)
|
|
|
|
def get_doc_types(self) -> list[dict]:
|
|
return self.main_tab.get_doc_types()
|
|
|
|
def set_doc_types(self, doc_types: list[dict]) -> None:
|
|
self.main_tab.set_doc_types(doc_types)
|
|
|
|
def set_allowed_document_types_hint(self, allowed_types: list[str]) -> None:
|
|
self.main_tab.set_allowed_document_types_hint(allowed_types)
|
|
|
|
def validate_doc_types(self) -> tuple[bool, str | None]:
|
|
return self.main_tab.validate_doc_types()
|
|
|
|
# --- TAB PROCESO DE LIMPIEZA ---
|
|
|
|
def set_on_cleaning_select_csv(self, callback):
|
|
self.cleaning_tab.set_on_select_csv(callback)
|
|
|
|
def set_on_cleaning_apply(self, callback):
|
|
self.cleaning_tab.set_on_apply(callback)
|
|
|
|
def set_on_cleaning_save(self, callback):
|
|
self.cleaning_tab.set_on_save(callback)
|
|
|
|
def set_cleaning_csv_path(self, path: str):
|
|
self.cleaning_tab.set_csv_path(path)
|
|
|
|
def set_cleaning_preview(self, df):
|
|
self.cleaning_tab.set_preview(df)
|
|
|
|
def populate_cleaning_columns(self, summaries):
|
|
self.cleaning_tab.populate_columns(summaries)
|
|
|
|
def get_cleaning_selected_rules(self) -> dict[str, list[str]]:
|
|
return self.cleaning_tab.get_selected_rules()
|
|
|
|
def set_cleaning_result(self, df, info_text: str = ""):
|
|
self.cleaning_tab.set_result(df, info_text)
|
|
|
|
def enable_cleaning_save(self, enabled: bool):
|
|
self.cleaning_tab.enable_save(enabled)
|
|
|
|
def enable_cleaning_controls(self, enabled: bool):
|
|
self.cleaning_tab.enable_controls(enabled)
|
|
|
|
def set_cleaning_status(self, text: str):
|
|
self.cleaning_tab.set_status(text)
|
|
|
|
def set_cleaning_delimiter_chip(self, delimiter: str):
|
|
self.cleaning_tab.set_delimiter_chip(delimiter)
|