Files
IPSExtractor/gui/view.py
T

356 lines
12 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
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")
# =========================================================================
# --- 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)
# ---------------------------------------------------------------------------
# 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()