feat: Introduce build and installation scripts for IPSExtractor, including a batch file for PyInstaller and Inno Setup integration, and add application preferences management for document types and retry logic in the GUI.

This commit is contained in:
erik
2026-04-02 16:32:36 -05:00
parent a1b0526080
commit 7a38b0843b
13 changed files with 845 additions and 62 deletions
+124 -2
View File
@@ -1,6 +1,7 @@
"""Pestaña Procesar Documentos de la aplicación GUI."""
import customtkinter as ctk
from gui.styles import AppStyles
from application.app_prefs import get_allowed_document_types
class MainProcessingTab:
@@ -112,7 +113,47 @@ class MainProcessingTab:
font=self.fonts["ui"]
)
self.radio_general_main.pack(side="left")
# Filtro por Radicados
self.filter_enabled_var = ctk.BooleanVar(value=False)
self.filter_check = ctk.CTkCheckBox(
self.frame_paths,
text="Procesar solo radicados seleccionados",
variable=self.filter_enabled_var,
font=self.fonts["ui"],
command=self._on_filter_toggle
)
self.filter_check.grid(row=4, column=0, columnspan=3, padx=15, pady=(4, 0), sticky="w")
self.filter_frame = ctk.CTkFrame(self.frame_paths, fg_color="transparent")
self.filter_frame.grid(row=5, column=0, columnspan=3, padx=15, pady=(2, 6), sticky="ew")
self.filter_frame.grid_columnconfigure(0, weight=1)
filter_hint = ctk.CTkLabel(
self.filter_frame,
text="Ingresa los radicados a procesar (uno por línea o separados por coma):",
font=self.fonts["ui"],
text_color=self.colors["text_muted"]
)
filter_hint.grid(row=0, column=0, sticky="w", pady=(0, 2))
self.filter_textbox = ctk.CTkTextbox(
self.filter_frame,
height=80,
font=self.fonts["mono"],
)
self.filter_textbox.grid(row=1, column=0, sticky="ew")
# Ocultar hasta que se active el checkbox
self.filter_frame.grid_remove()
def _on_filter_toggle(self):
"""Muestra u oculta el área de texto del filtro."""
if self.filter_enabled_var.get():
self.filter_frame.grid()
else:
self.filter_frame.grid_remove()
def _create_config_section(self):
"""Crea la sección de configuración de tipos de documento y TIFF."""
self.frame_config = ctk.CTkFrame(self.scroll_container)
@@ -139,6 +180,15 @@ class MainProcessingTab:
font=self.fonts["section"]
)
self.doc_types_label.grid(row=0, column=0, sticky="w")
allowed_types_text = ", ".join(get_allowed_document_types())
self.doc_types_hint = ctk.CTkLabel(
self.doc_types_header,
text=f"Tipos válidos para documento_forzate: {allowed_types_text}. NIT no aplica.",
font=self.fonts["ui"],
text_color=self.colors["text_muted"],
)
self.doc_types_hint.grid(row=1, column=0, sticky="w", pady=(2, 0))
self.add_doc_type_btn = ctk.CTkButton(
self.doc_types_header,
@@ -147,7 +197,7 @@ class MainProcessingTab:
width=120,
height=28
)
self.add_doc_type_btn.grid(row=0, column=1, sticky="e")
self.add_doc_type_btn.grid(row=0, column=1, rowspan=2, sticky="e")
self.add_doc_type_btn.configure(command=self._add_doc_type_row)
# Lista de Tipos
@@ -161,6 +211,16 @@ class MainProcessingTab:
# Configuración Avanzada (TIFF Range)
self.frame_advanced = ctk.CTkFrame(self.frame_config, fg_color="transparent")
self.frame_advanced.grid(row=1, column=0, sticky="ew", padx=10, pady=(0, 2))
self.frame_advanced.grid_columnconfigure(0, weight=1)
self.retry_empty_var = ctk.BooleanVar(value=False)
self.retry_empty_check = ctk.CTkCheckBox(
self.frame_advanced,
text="Reintentar si la fila sale vacía (máx. 2 intentos)",
variable=self.retry_empty_var,
font=self.fonts["ui"]
)
self.retry_empty_check.grid(row=0, column=0, columnspan=4, padx=5, pady=(0, 6), sticky="w")
self.tiff_start_label = ctk.CTkLabel(
self.frame_advanced,
@@ -323,6 +383,16 @@ class MainProcessingTab:
return v if v in ("radicados", "general") else "radicados"
except Exception:
return "radicados"
def set_process_mode(self, mode: str) -> None:
"""Establece el modo de procesamiento y refresca la UI."""
try:
normalized = (mode or "").strip().lower()
self.mode_var.set(normalized if normalized in ("radicados", "general") else "radicados")
self.update_ui_for_mode()
except Exception:
self.mode_var.set("radicados")
self.update_ui_for_mode()
def get_keywords(self) -> str:
"""Devuelve los keywords."""
@@ -347,6 +417,50 @@ class MainProcessingTab:
return int(value) if value else 0
except Exception:
return 0
def get_retry_empty_enabled(self) -> bool:
"""Indica si se debe reintentar cuando la fila solo trae el radicado."""
try:
return bool(self.retry_empty_var.get())
except Exception:
return False
def set_retry_empty_enabled(self, enabled: bool) -> None:
"""Establece el reintento automático por fila vacía."""
self.retry_empty_var.set(bool(enabled))
def get_radicado_filter_enabled(self) -> bool:
"""Indica si el filtro de radicados está activo."""
try:
return bool(self.filter_enabled_var.get())
except Exception:
return False
def get_radicado_filter_list(self) -> list[str]:
"""Devuelve la lista de radicados del filtro (sin blancos ni duplicados)."""
try:
raw = self.filter_textbox.get("1.0", "end")
items = []
for part in raw.replace(",", "\n").splitlines():
s = part.strip()
if s:
items.append(s)
return list(dict.fromkeys(items)) # preserva orden, elimina duplicados
except Exception:
return []
def set_radicado_filter_enabled(self, enabled: bool) -> None:
"""Activa o desactiva el filtro y refresca la UI."""
self.filter_enabled_var.set(bool(enabled))
self._on_filter_toggle()
def set_radicado_filter_list(self, radicados: list[str]) -> None:
"""Carga una lista de radicados en el área de texto del filtro."""
try:
self.filter_textbox.delete("1.0", "end")
self.filter_textbox.insert("1.0", "\n".join(radicados))
except Exception:
pass
def show_tiff_range_controls(self, show: bool) -> None:
"""Muestra u oculta los controles de rango TIFF."""
@@ -398,6 +512,11 @@ class MainProcessingTab:
if not seen:
return False, "Debe haber al menos un tipo de documento."
return True, None
def set_allowed_document_types_hint(self, allowed_types: list[str]) -> None:
"""Actualiza el texto de ayuda de tipos válidos."""
text = ", ".join(allowed_types)
self.doc_types_hint.configure(text=f"Tipos válidos para documento_forzate: {text}. NIT no aplica.")
def update_ui_for_mode(self):
"""Actualiza la visibilidad de componentes según el modo seleccionado."""
@@ -445,6 +564,9 @@ class MainProcessingTab:
self.csv_button.configure(state=state)
self.csv_entry.configure(state=state)
self.add_doc_type_btn.configure(state=state)
self.retry_empty_check.configure(state=state)
self.filter_check.configure(state=state)
self.filter_textbox.configure(state=state)
for row in self._doc_type_rows:
row["name_entry"].configure(state=state)