Código fuente para logica.gestor

import os
from pathlib import Path
import tkinter as tk
from tkinter import filedialog
from app.comunes.globales import gb
from app.comunes.idioma import leng


[documentos] class Gestor: """ Clase encargada de coordinar los componentes y facilitar la comunicación entre la tabla, los componentes y el generador. """ def __init__(self): """ Constructor de la clase Gestor. Contiene los componentes principales de la aplicación y coordina sus tareas. """ self.raiz = None self.tabla = None self.generador = None self.interface = None
[documentos] def enlazar_raiz(self, raiz_activa): """ Almacena la instancia de la ventana raiz. :param raiz: Ventana principal de la aplicación. :type raiz: Tk :return: None """ self.raiz = raiz_activa
[documentos] def enlazar_tabla(self, tabla_activa): """ Almacena la instancia de la tabla. :param tabla: Componente que almacena los datos ingresados. :type tabla: Tabla :return: None """ self.tabla = tabla_activa
[documentos] def enlazar_generador(self, generador_activo): """ Almacena la instancia del generador. :param generador_activo: Componente que produce y exporta la tabla. :type generador_activo: Generador :return: None """ self.generador = generador_activo
[documentos] def enlazar_interface(self, interface_activa): """ Almacena la instancia de la interface. :param interface_activa: Componente que contiene los widgets de ingreso de datos. :type interface_activa: Interface :return: None """ self.interface = interface_activa
[documentos] def recibir(self, origen:str, mensaje) -> bool: """ Enruta los mensajes al método correspondiente. Si la gestión fue exitosa (se logró almacenar los datos en la tabla), actualiza la vista. :param origen: Módulo que efectúa la llamada. :type origen: str :param mensaje: Contenido a gestionar. :type mensaje: str, list :return gestion: True si fue exitosa, False en caso contrario. :rtype gestion: bool """ gestion:bool = False if origen == "m_ruta": gestion = self._gestion_ruta(mensaje) # str elif origen == "m_encabezado": gestion = self._gestion_encabezado(mensaje) # str elif origen == "m_opciones_tabla": gestion = self._gestion_opciones_tabla(mensaje) # list elif origen == "m_campo_booleano": gestion = self._gestion_campo_booleano(mensaje) # list elif origen == "m_campo_texto": gestion = self._gestion_campo_texto(mensaje) # list elif origen == "m_campo_numerico": gestion = self._gestion_campo_numerico(mensaje) # list elif origen == "m_exportar": gestion = self._gestion_exportar(mensaje) # str if gestion: self.actualizar_vista() return gestion
[documentos] def actualizar_vista(self): """ Envía a la vista los datos almacenados en la tabla. :return: None """ self.interface.modulo_ver_datos.insertar_datos(self.tabla.obtener_datos())
[documentos] def ver_encabezado(self) -> list: """ Retorna los campos almacenados en la tabla. :return encabezado: Campos ingresados. :rtype encabezado: list """ encabezado:list = self.tabla.obtener_encabezado() return encabezado
[documentos] def borrar_encabezado(self): """ Vacía la lista de campos, las opciones que la utilizan y luego actualiza la vista. :return: None """ self.tabla.borrar_encabezado() self.tabla.borrar_todos_los_campos() self.actualizar_opciones() self.actualizar_vista()
[documentos] def actualizar_opciones(self): """ Actualiza todos los componentes que utilizan los campos ingresados en la tabla. :return: None """ self.interface.modulo_opciones_tabla.actualizar_opciones_criterio() self.interface.modulo_campo_booleano.actualizar_opciones_campo() self.interface.modulo_campo_texto.actualizar_opciones_campo() self.interface.modulo_campo_numerico.actualizar_opciones_campo() self.interface.modulo_borrar_campo.actualizar_opciones_borrado()
[documentos] def ver_campos_definidos(self) -> list: """ Retorna una lista con los nombres de los campos almacenados en la tabla. :return: Lista de los nombres de los campos almacenados. :rtype: list """ return self.tabla.campos_definidos()
[documentos] def ver_campos_sin_definir(self) -> list: """ Retorna una lista con los nombres de los campos sin definir. :return: Lista de los nombres de los campos sin definir. :rtype: list """ a = set(self.ver_encabezado()) b = set(self.ver_campos_definidos()) return list(a - b)
[documentos] def restablecer_aplicacion(self): """ Limpia el texto de las entradas, actualiza las opciones y establece el enfoque del teclado en el widget principal del módulo ruta. :return: None """ self.interface.modulo_ruta.limpiar_entrada() self.interface.modulo_encabezado.limpiar_entrada() self.interface.modulo_campo_texto.limpiar_entrada() self.interface.modulo_campo_numerico.limpiar_entrada() self.interface.modulo_opciones_tabla.limpiar_entrada() self.interface.modulo_exportar.limpiar_entrada() self.actualizar_opciones() self.interface.modulo_ruta.enfocar()
[documentos] def vaciar_tabla(self): """ Vacia todos los datos ingresados a la tabla. :return: None """ self.tabla.restablecer_tabla()
[documentos] def vaciar_campo(self, nombre_campo:str): """ Borra los valores registrados en la tabla del campo especificado. :param nombre_campo: Campo especificado. :type nombre_campo: str :return: None """ if nombre_campo in self.tabla.obtener_campos_booleanos(): self.tabla.borrar_campo_booleano(nombre_campo) elif nombre_campo in self.tabla.obtener_campos_texto(): self.tabla.borrar_campo_texto(nombre_campo) elif nombre_campo in self.tabla.obtener_campos_enteros(): self.tabla.borrar_campo_entero(nombre_campo) elif nombre_campo in self.tabla.obtener_campos_decimales(): self.tabla.borrar_campo_real(nombre_campo)
[documentos] def _gestion_ruta(self, mensaje:str) -> bool: """ Gestiona la validación de la ruta ingresada. Si fue exitosa retorna True, caso contrario notifica el error y retorna False. :param mensaje: Ruta de salida ingresada. :type mensaje: str :return res: True si la gestión fue exitosa, de lo contrario False. :rtype res: bool """ res:bool = False if self._es_ruta_valida(mensaje): res = True else: self._ventana_dialogo( leng.tex['err_ruta_invalida'], leng.tex['err_vt_ruta_invalida'] ) return res
[documentos] def _es_ruta_valida(self, ruta:str) -> bool: """ Verifica la validez de la ruta ingresada o la existencia del directorio de salida. Si la verificación es positiva, registra la ruta en la tabla. :param ruta: Ruta de salida ingresada. :type ruta: str :return res: True si la verificación fue exitosa, de lo contrario False. :rtype res: bool """ res:bool = False ruta_final = Path(ruta) if ruta.endswith(".csv") and ruta_final.parent.is_dir(): res = True elif ruta_final.is_dir(): ruta_final = ruta_final / leng.tex['ruta_archivo'] res = True if res: self.tabla.establecer_ruta(ruta_final) return res
[documentos] def _gestion_encabezado(self, mensaje:str) -> bool: """ Notifica si el encabezado ingresado está vacío. Si no lo está, lo procesa y registra en la tabla. Elimina aquellos campos definidos que no estén en el nuevo encabezado y actualiza las opciones de los componentes correspondientes. :param mensaje: Encabezado ingresado. :type mensaje: str :return res: True si la gestión fue exitosa, de lo contrario False. :rtype res: bool """ res:bool = False lista_encabezado:list = [] if len(mensaje) < 1: self._ventana_dialogo( leng.tex['err_encabezado'], leng.tex['err_vt_encabezado'] ) return res else: lista_encabezado = self._limpiar_lista(mensaje) # Limita el maximo de caracteres de un campo for campo in lista_encabezado: if len(campo) > 100: # Umbral self._ventana_dialogo( leng.tex['err_long_encabezado'], leng.tex['err_vt_long_encabezado'] ) return res self.tabla.establecer_encabezado(lista_encabezado) res = True for campo in list(self.tabla.obtener_campos_booleanos()): if campo not in lista_encabezado: self.tabla.borrar_campo_booleano(campo) for campo in list(self.tabla.obtener_campos_texto()): if campo not in lista_encabezado: self.tabla.borrar_campo_texto(campo) for campo in list(self.tabla.obtener_campos_enteros()): if campo not in lista_encabezado: self.tabla.borrar_campo_entero(campo) for campo in list(self.tabla.obtener_campos_decimales()): if campo not in lista_encabezado: self.tabla.borrar_campo_real(campo) self.interface.modulo_campo_booleano.actualizar_opciones_campo() self.interface.modulo_campo_texto.actualizar_opciones_campo() self.interface.modulo_campo_numerico.actualizar_opciones_campo() self.interface.modulo_borrar_campo.actualizar_opciones_borrado() return res
[documentos] def _limpiar_lista(self, cadena:str) -> list: """ Produce una lista de items únicos y no vacíos a partir de una cadena. :param cadena: Cadena con valores separados por coma. :param cadena: str :return lista_salida: Lista de items únicos. :rtype lista_salida: list """ lista_salida:list = cadena.split(',') lista_salida = [item.strip() for item in lista_salida] lista_salida = [item for item in lista_salida if item != " "] lista_aux:list = [] [lista_aux.append(e) for e in lista_salida if e not in lista_aux] lista_salida = lista_aux return lista_salida
[documentos] def _gestion_opciones_tabla(self, mensaje:list) -> bool: """ Valida los valores definidos. Si la validación es exitosa los registra en la tabla; caso contrario, notifica el error. :param mensaje: Opciones seleccionadas. :type mensaje: list :return res: True si la gestión fue exitosa, de lo contrario False. :rtype res: bool """ res:bool = False delimitador:str = mensaje[0] orden:str = mensaje[1] criterio_campo:str = mensaje[2] modo:str = mensaje[3] semilla:str = mensaje[4] encabezado:list = self.tabla.obtener_encabezado() if "pandas" in orden: try: import pandas except Exception as e: m_error:str = leng.tex['err_import_1'] + f"\n{e}\n" + leng.tex['err_import_2'] m_error += leng.tex['err_import_3'] self._ventana_dialogo(m_error, leng.tex['err_vt_import']) return res if modo == "secrets": self.tabla.borrar_campos_decimales() if len(delimitador) != 1: self._ventana_dialogo( leng.tex['err_delimitador'], leng.tex['err_vt_delimitador'] ) return res elif (not criterio_campo in encabezado and orden != leng.tex['orden_opc_2']): self._ventana_dialogo( leng.tex['err_criterio'], leng.tex['err_vt_criterio'] ) return res else: self.tabla.establecer_delimitador(delimitador) self.tabla.establecer_orden(orden) self.tabla.establecer_criterio(criterio_campo) self.tabla.establecer_modo(modo) if len(semilla) > 100: # Umbral self._ventana_dialogo( leng.tex['err_semilla'], leng.tex['err_vt_semilla'] ) return res if semilla == "": self.tabla.establecer_semilla(leng.tex['semilla_def']) else: self.tabla.establecer_semilla(semilla) res = True return res
[documentos] def _gestion_campo_booleano(self, mensaje:list) -> bool: """ Verifica las condiciones mínimas para registrar el campo. Si la validación es exitosa lo registra en la tabla; caso contrario, notifica el error. :param mensaje: Campo y opción seleccionada. :type mensaje: list :return res: True si la gestión fue exitosa, de lo contrario False. :rtype res: bool """ res:bool = False nombre_campo = mensaje[0] valores_bool = mensaje[1] condiciones:bool = self._condiciones_basicas(nombre_campo) if condiciones: self.tabla.establecer_campo_booleano(nombre_campo, valores_bool) res = True return res
[documentos] def _gestion_campo_texto(self, mensaje:list) -> bool: """ Verifica las condiciones mínimas para registrar el campo y procesa las alternativas de texto ingresadas. Si la operación es exitosa lo registra en la tabla; caso contrario, notifica el error. :param mensaje: Campo y alternativas de texto. :type mensaje: list :return res: True si la gestión fue exitosa, de lo contrario False. :rtype res: bool """ res:bool = False nombre_campo:str = mensaje[0] valores_posibles:str = mensaje[1] condiciones:bool = self._condiciones_basicas(nombre_campo) if condiciones: valores:list = self._limpiar_lista(valores_posibles) valores = [item for item in valores if item != ""] for alternativa in valores: if len(alternativa) > 500: # Umbral self._ventana_dialogo( leng.tex['err_long_alter_tex'], leng.tex['err_vt_long_alter_tex'] ) return res if any(valor != "" for valor in valores): self.tabla.establecer_campo_texto(nombre_campo, valores) res = True else: self._ventana_dialogo( leng.tex['err_no_valores_tex'], leng.tex['err_vt_no_valores_tex'] ) return res
[documentos] def _condiciones_basicas(self, nombre_campo:str) -> bool: """ Verifica las condiciones mínimas que debe cumplir un campo para ser registrado en la tabla. :param nombre_campo: Campo a evaluar. :type nombre_campo: str :return res: True si la verificación fue exitosa, de lo contrario False. :rtype res: bool" """ res:bool = False campos_definidos:list = self.tabla.campos_definidos() if nombre_campo == "": self._ventana_dialogo( leng.tex['err_campo_def'], leng.tex['err_vt_campo_def'] ) return res elif nombre_campo not in self.tabla.obtener_encabezado(): self._ventana_dialogo( leng.tex['err_campo_inexistente'], leng.tex['err_vt_campo_inex'] ) return res elif nombre_campo in campos_definidos: self._ventana_dialogo( leng.tex['err_campo_repetido'], leng.tex['err_vt_campo_repe'] ) return res else: res = True return res
[documentos] def _gestion_campo_numerico(self, mensaje:list) -> bool: """ Verifica las condiciones mínimas para registrar el campo y valida el rango y el valor de redondeo. Si la operación es exitosa lo registra en la tabla; caso contrario, notifica el error. :param mensaje: Campo, rango, tipo numérico y valor de redondeo. :type mensaje: list :return res: True si la gestión fue exitosa, de lo contrario False. :rtype res: bool """ res:bool = False nombre_campo:str = mensaje[0] rango_inicio:str = mensaje[1].strip() rango_fin:str = mensaje[2].strip() tipo_numerico:str = mensaje[3] redondeo: str = mensaje[4] digitos_inicio:int = len(rango_inicio) digitos_fin:int = len(rango_fin) redondear:int = 0 if not redondeo.isdigit(): self._ventana_dialogo( leng.tex['err_redondeo'], leng.tex['err_vt_redondeo'] ) return res if (redondeo.isdigit() and int(redondeo) >= 0 and int(redondeo) < 16): # Umbral redondear = int(redondeo) else: self._ventana_dialogo( leng.tex['err_redondeo'], leng.tex['err_vt_redondeo'] ) return res if rango_inicio == '' or rango_fin == '': self._ventana_dialogo( leng.tex['err_rango_invalido'], leng.tex['err_vt_rango_invalido'] ) return res condiciones_ok:bool = self._condiciones_basicas(nombre_campo) if not condiciones_ok: return res else: rango_i:int = self._preservar_signo(rango_inicio) rango_f:int = self._preservar_signo(rango_fin) if type(rango_i) is int and type(rango_f) is int: if rango_i > rango_f: self._ventana_dialogo( leng.tex['err_rango_menor'], leng.tex['err_vt_rango_menor'] ) return res elif (rango_i < rango_f and digitos_inicio <= 100 and digitos_fin <= 100): # Umbral rango:tuple = (rango_i, rango_f, redondear) if tipo_numerico == leng.tex['opc_num_int']: # Entero self.tabla.establecer_campo_entero(nombre_campo, rango) res = True elif tipo_numerico == leng.tex['opc_num_float']: # Flotante self.tabla.establecer_campo_decimal(nombre_campo, rango) res = True elif rango_i == rango_f: self._ventana_dialogo( leng.tex['err_rango_invalido'], leng.tex['err_vt_rango_invalido'] ) return res else: self._ventana_dialogo( leng.tex['err_rango_invalido'], leng.tex['err_vt_rango_invalido'] ) return res else: self._ventana_dialogo( leng.tex['err_rango_invalido'], leng.tex['err_vt_rango_invalido'] ) return res
[documentos] def _preservar_signo(self, numero:str): """ Evalúa y convierte una cadena a entero si es un número válido; de lo contrario, retorna la cadena original. :param numero: Cadena a evaluar. :type numero: str :return res: Resultado de la operación. :rtype res: int, str """ res:int = 0 if ((not numero.isdigit() and numero[0] == '-' and numero[1:].isdigit()) or numero.isdigit()): res = int(numero) else: res = numero return res
[documentos] def _gestion_exportar(self, mensaje:str) -> bool: """ Envía al generador los datos registrados en la tabla si se cumplen las condiciones de exportación. Notifica si la operación fue existosa. En caso de que alguna condición no se cumpla, notifica el error. :param mensaje: Número de filas ingresado. :type mensaje: str :return res: True si la gestión fue exitosa, de lo contrario False. :rtype res: bool """ res:bool = False num_filas:int = 0 if mensaje.isdigit(): num_filas = int(mensaje) else: self._ventana_dialogo(leng.tex['err_filas'], leng.tex['err_vt_filas']) return res if num_filas > 1000000: # Umbral self._ventana_dialogo( leng.tex['err_umbral_filas'], leng.tex['err_vt_umbral_filas'] ) return res try: # Verifica la validez de la ruta y los permisos de escritura with open(self.tabla.obtener_ruta(), "w", newline="", encoding="utf-8"): pass # Si no hay error sigue except FileNotFoundError: self._ventana_dialogo( leng.tex['err_ruta_ingresada'], leng.tex['err_vt_ruta_ingresada'] ) return res except PermissionError: self._ventana_dialogo( leng.tex['err_escritura'], leng.tex['vt_err_escritura'] ) return res except Exception as e: self._ventana_dialogo( f'{leng.tex["err_ruta_inesperado"]}: {e}', leng.tex['err_vt_ruta_inesperado'] ) return res # Chequea que no haya inconsistencias en el tipo de orden if self.tabla.obtener_orden() != leng.tex['orden_opc_2']: if self.tabla.obtener_criterio() not in self.tabla.campos_definidos(): self._ventana_dialogo( leng.tex['err_criterio'], leng.tex['err_vt_criterio'] ) return res # Previene que se intente generar decimales con secrets if self.tabla.obtener_modo() == "secrets": if self.tabla.obtener_campos_decimales(): self._ventana_dialogo( leng.tex['err_secrets'], leng.tex['err_vt_secrets'] ) return res # Envia los datos al generador if num_filas: d_aux:dict = vars(self.tabla) d_aux["_filas"] = num_filas res = self.generador.crear_tabla(d_aux) else: self._ventana_dialogo( leng.tex['err_filas'], leng.tex['err_vt_filas'] ) # Respuesta de tabla generada ok if res: self._ventana_dialogo( leng.tex['ok_tabla'], leng.tex['vt_ok_tabla'], gb.ESTILO_BTN_OK ) else: self._ventana_dialogo( leng.tex['err_ruta_inesperado'], leng.tex['err_vt_ruta_inesperado'] ) return res