aef8791151
- Added Appshell component with responsive navbar and main content area - Integrated ColorSchemeToggle for light/dark mode switching - Created Welcome component with styled title and introductory text - Developed ChatPage for LLM interaction with WebSocket support - Implemented Biblioteca for managing notes with rich text editor - Added LoginPage for user authentication with error handling - Introduced MessageList and MessageBubble components for chat messages - Styled components with CSS modules for consistent design
196 lines
6.6 KiB
Python
196 lines
6.6 KiB
Python
import os
|
|
from dotenv import load_dotenv
|
|
from sqlalchemy import Table, Column, String, Text, MetaData
|
|
from pgvector.sqlalchemy import Vector
|
|
from sqlalchemy.orm import registry, Session
|
|
from domains.TextManager.nota import Nota
|
|
from domains.ConexionSql.Base_conexion import ConexionBase
|
|
from typing import Tuple
|
|
import re
|
|
|
|
|
|
from domains.ArquitectureLayer.Mapper import Mapper_base
|
|
from domains.ArquitectureLayer.Model import Model_base
|
|
from domains.ArquitectureLayer.Repo import Repo_base
|
|
|
|
from domains.Credenciales.postgres_credencial import PostgresCredencial # Asegúrate de tener esta clase implementada correctamente
|
|
|
|
|
|
titulo = os.getenv('DB_TITLE')
|
|
usuario = os.getenv('DB_USER')
|
|
passwrd = os.getenv('DB_PASSWORD')
|
|
host = os.getenv('DB_HOST')
|
|
port = os.getenv('DB_PORT')
|
|
db_name = os.getenv('DB_NAME')
|
|
|
|
|
|
db_credencial = PostgresCredencial(
|
|
titulo=titulo,
|
|
user=usuario,
|
|
password=passwrd,
|
|
host=host,
|
|
port=port,
|
|
dbname=db_name
|
|
)
|
|
|
|
# from entrypoint.init_db import db_credencial
|
|
from domains.Logger.logger_db import LoggerDB, logger
|
|
LoggerDB(db_credencial, "logger_textos", created_by="sistema")
|
|
|
|
|
|
from domains.base import Base # Este es tu declarative_base()
|
|
|
|
# ----------------------
|
|
# Cargar .env
|
|
# ----------------------
|
|
from entrypoint import ENV_PATH
|
|
load_dotenv(ENV_PATH)
|
|
|
|
# ----------------------
|
|
# REGISTRO DINÁMICO PARA TABLAS
|
|
# ----------------------
|
|
mapper_registry = registry()
|
|
|
|
# ----------------------
|
|
# FUNCIONES AUXILIARES
|
|
# ----------------------
|
|
|
|
|
|
def generar_tabla_nota_para_biblioteca(biblioteca_nombre: str, vector_dim: int, metadata: MetaData) -> Tuple[Table, type]:
|
|
"""
|
|
Genera una tabla dinámica y modelo ORM para una biblioteca dada, con campos vectoriales y campos del sistema.
|
|
"""
|
|
try:
|
|
logger.info(f"Generando tabla para biblioteca: '{biblioteca_nombre}' con dimensión de vector: {vector_dim}")
|
|
|
|
# Nombre SQL-safe
|
|
nombre_tabla = re.sub(r"[^a-zA-Z0-9_]", "_", biblioteca_nombre.strip().lower())
|
|
logger.debug(f"Nombre de tabla SQL-safe: '{nombre_tabla}'")
|
|
|
|
# Modelo ORM dinámico
|
|
class NotaModel(Base, Model_base):
|
|
__tablename__ = nombre_tabla
|
|
__table_args__ = {"extend_existing": True}
|
|
|
|
id = Column(String, primary_key=True)
|
|
titulo = Column(String, nullable=False)
|
|
tags = Column(String)
|
|
conexiones = Column(String)
|
|
texto = Column(Text)
|
|
resumen = Column(Text)
|
|
vector = Column(Vector(vector_dim), nullable=True)
|
|
vector_resumen = Column(Vector(vector_dim), nullable=True)
|
|
|
|
logger.info(f"Modelo ORM 'NotaModel' creado para la tabla '{nombre_tabla}'")
|
|
logger.debug(f"Columnas del modelo: {[c.name for c in NotaModel.__table__.columns]}")
|
|
logger.debug(f"Tipos de columnas: {[str(c.type) for c in NotaModel.__table__.columns]}")
|
|
logger.debug(f"Claves primarias: {[c.name for c in NotaModel.__table__.primary_key]}")
|
|
|
|
return NotaModel.__table__, NotaModel
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error al generar la tabla y modelo ORM para '{biblioteca_nombre}': {e}")
|
|
raise
|
|
|
|
# ----------------------
|
|
# MAPPER
|
|
# ----------------------
|
|
|
|
class NotaMapper(Mapper_base[Nota, object]): # Usa `object` si el modelo es dinámico
|
|
@staticmethod
|
|
def to_model(nota: Nota):
|
|
return {
|
|
"id": nota.id,
|
|
"titulo": nota.titulo,
|
|
"tags": ",".join(nota.tags),
|
|
"conexiones": ",".join(nota.conexiones),
|
|
"texto": nota.texto,
|
|
"resumen": nota.resumen,
|
|
"vector": nota.vector,
|
|
"vector_resumen": nota.vector_resumen
|
|
}
|
|
|
|
@staticmethod
|
|
def from_model(model) -> Nota:
|
|
return Nota(
|
|
id=model.id,
|
|
titulo=model.titulo,
|
|
tags=model.tags.split(",") if model.tags else [],
|
|
conexiones=model.conexiones.split(",") if model.conexiones else [],
|
|
texto=model.texto,
|
|
resumen=model.resumen,
|
|
vector=list(model.vector) if model.vector is not None else None,
|
|
vector_resumen=list(model.vector_resumen) if model.vector_resumen is not None else None
|
|
)
|
|
|
|
@staticmethod
|
|
def to_dict(nota: Nota) -> dict:
|
|
return {
|
|
"id": nota.id,
|
|
"titulo": nota.titulo,
|
|
"tags": ",".join(nota.tags),
|
|
"conexiones": ",".join(nota.conexiones),
|
|
"texto": nota.texto,
|
|
"resumen": nota.resumen,
|
|
"vector": nota.vector,
|
|
"vector_resumen": nota.vector_resumen
|
|
}
|
|
|
|
@staticmethod
|
|
def from_dict(data: dict) -> Nota:
|
|
return Nota(
|
|
id=data["id"],
|
|
titulo=data["titulo"],
|
|
tags=data["tags"].split(",") if data.get("tags") else [],
|
|
conexiones=data["conexiones"].split(",") if data.get("conexiones") else [],
|
|
texto=data["texto"],
|
|
resumen=data["resumen"],
|
|
vector=data.get("vector"),
|
|
vector_resumen=data.get("vector_resumen")
|
|
)
|
|
|
|
# ----------------------
|
|
# REPO
|
|
# ----------------------
|
|
|
|
class NotaRepo(Repo_base):
|
|
def __init__(self, session: Session, modelo_nota: type):
|
|
if modelo_nota is None:
|
|
raise ValueError("No se puede instanciar NotaRepo sin un modelo válido de nota.")
|
|
super().__init__(session=session, modelo=modelo_nota, mapper=NotaMapper)
|
|
|
|
# ------------------------
|
|
# Métodos personalizados
|
|
# ------------------------
|
|
|
|
def get_by_tag(self, tag: str) -> list[Nota]:
|
|
query = self.session.query(self.Modelo).filter(self.Modelo.tags.contains([tag]))
|
|
models = query.all()
|
|
return self.Mapper.from_model_list(models)
|
|
|
|
def search_by_text(self, texto: str) -> list[Nota]:
|
|
query = self.session.query(self.Modelo).filter(self.Modelo.texto.ilike(f'%{texto}%'))
|
|
models = query.all()
|
|
return self.Mapper.from_model_list(models)
|
|
|
|
def get_paginated(self, offset: int = 0, limit: int = 10) -> list[Nota]:
|
|
models = self.session.query(self.Modelo).offset(offset).limit(limit).all()
|
|
return self.Mapper.from_model_list(models)
|
|
|
|
def update(self, id_: str, nota_actualizada: Nota) -> bool:
|
|
model = self.session.get(self.Modelo, id_)
|
|
if not model:
|
|
return False
|
|
|
|
# Campos de dominio
|
|
model.titulo = nota_actualizada.titulo
|
|
model.texto = nota_actualizada.texto
|
|
model.tags = nota_actualizada.tags
|
|
model.conexiones = nota_actualizada.conexiones
|
|
model.resumen = nota_actualizada.resumen
|
|
|
|
# Actualización de campos de sistema
|
|
model.sys_version = (model.sys_version or 1) + 1
|
|
|
|
self.session.commit()
|
|
return True |