Notas en frontend funcionando y pudiendo subir mas por sus endpoints

This commit is contained in:
2025-05-11 02:30:55 +02:00
parent b34d52036e
commit 712bd877b8
14 changed files with 795 additions and 41 deletions
+231
View File
@@ -0,0 +1,231 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 8,
"id": "255345d5",
"metadata": {},
"outputs": [],
"source": [
"from src.TextManager.biblioteca import Biblioteca\n",
"from src.TextManager.biblioteca_mmr import BibliotecaRepo\n",
"from src.Llms.Embedders.Openai_embedder import OpenAIEmbedder\n",
"from src.ApiKeys.openai_apikey_mmr import OpenAICredencialRepo\n",
"from src.ConexionSql.Postgres_conexion import PostgresConexion\n",
"from src.TextManager.nota import Nota\n",
"from src.TextManager.notas_biblioteca_mmr import generar_tabla_nota_para_biblioteca, NotaRepo\n",
"from sqlalchemy import MetaData\n"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "b414a66c",
"metadata": {},
"outputs": [],
"source": [
"def agregar_nota_a_biblioteca(\n",
" conexion: PostgresConexion,\n",
" biblioteca_id: str,\n",
" titulo: str,\n",
" texto: str = \"\",\n",
" tags: list[str] = None,\n",
" conexiones: list[str] = None,\n",
" resumen: str = \"\"\n",
"):\n",
" print(\"[INFO] Iniciando el proceso de agregar nota a la biblioteca...\")\n",
"\n",
" # Obtener la biblioteca\n",
" print(f\"[INFO] Buscando biblioteca con ID: {biblioteca_id}\")\n",
" repo_biblioteca = BibliotecaRepo(conexion)\n",
" biblioteca = repo_biblioteca.get_by_id(biblioteca_id)\n",
" if biblioteca is None:\n",
" print(f\"[ERROR] No se encontró la biblioteca con ID {biblioteca_id}\")\n",
" raise ValueError(f\"No se encontró la biblioteca con ID {biblioteca_id}\")\n",
" print(f\"[INFO] Biblioteca encontrada: {biblioteca.nombre} (vector_dim={biblioteca.vector_dim})\")\n",
"\n",
" # Crear objeto Nota\n",
" print(f\"[INFO] Creando objeto Nota con título: {titulo}\")\n",
" nota = Nota(\n",
" titulo=titulo,\n",
" texto=texto,\n",
" tags=tags or [],\n",
" conexiones=conexiones or [],\n",
" resumen=resumen or \"\",\n",
" # vector=biblioteca.embedder.embed_text(texto),\n",
" # vector_resumen=biblioteca.embedder.embed_text(resumen) if resumen else None\n",
" )\n",
" # Mostrar atributos seguros\n",
" print(\n",
" f\"[DEBUG] Nota creada: titulo='{nota.titulo}', \"\n",
" f\"texto_len={len(nota.texto)}, \"\n",
" f\"tags={len(nota.tags)}, \"\n",
" f\"conexiones={len(nota.conexiones)}, \"\n",
" f\"resumen_len={len(nota.resumen)}\"\n",
" )\n",
"\n",
" # Preparar tabla y modelo de nota\n",
" print(f\"[INFO] Generando tabla y modelo de Nota para la biblioteca: {biblioteca.nombre}\")\n",
" metadata = MetaData()\n",
" tabla, ModeloNota = generar_tabla_nota_para_biblioteca(\n",
" biblioteca.nombre,\n",
" biblioteca.vector_dim,\n",
" metadata\n",
" )\n",
" print(f\"[INFO] Creando tabla en la base de datos si no existe...\")\n",
" metadata.create_all(conexion.get_engine())\n",
" print(f\"[INFO] Tabla '{tabla.name}' verificada/creada.\")\n",
"\n",
" # Guardar la nota\n",
" print(f\"[INFO] Guardando nota en la base de datos...\")\n",
" repo_nota = NotaRepo(conexion.get_session(), ModeloNota)\n",
" nota_id = repo_nota.add(nota)\n",
" print(f\"[INFO] Nota guardada con ID: {nota_id}\")\n",
"\n",
" resultado = {\n",
" \"mensaje\": f\"Nota '{titulo}' agregada con éxito a la biblioteca '{biblioteca.nombre}'.\",\n",
" \"nota_id\": nota_id\n",
" }\n",
"\n",
" print(f\"[SUCCESS] {resultado['mensaje']}\")\n",
" return resultado\n"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "8e57e511",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[INFO] Iniciando el proceso de agregar nota a la biblioteca...\n",
"[INFO] Buscando biblioteca con ID: BBLI20250511-a91dbb2168172979414\n",
"[INFO] Biblioteca encontrada: biblio_Pruebas_1 (vector_dim=3072)\n",
"[INFO] Creando objeto Nota con título: sajdhasjdhasjdh\n",
"[DEBUG] Nota creada: titulo='sajdhasjdhasjdh', texto_len=0, tags=0, conexiones=0, resumen_len=0\n",
"[INFO] Generando tabla y modelo de Nota para la biblioteca: biblio_Pruebas_1\n",
"[INFO] Creando tabla en la base de datos si no existe...\n",
"[INFO] Tabla 'biblio_pruebas_1' verificada/creada.\n",
"[INFO] Guardando nota en la base de datos...\n",
"[INFO] Nota guardada con ID: NOTA20250511-04dbb203a9126228444\n",
"[SUCCESS] Nota 'sajdhasjdhasjdh' agregada con éxito a la biblioteca 'biblio_Pruebas_1'.\n"
]
},
{
"data": {
"text/plain": [
"{'mensaje': \"Nota 'sajdhasjdhasjdh' agregada con éxito a la biblioteca 'biblio_Pruebas_1'.\",\n",
" 'nota_id': 'NOTA20250511-04dbb203a9126228444'}"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from entrypoint.init_db import db_credencial\n",
"conexion_admin = PostgresConexion(db_credencial)\n",
"\n",
"agregar_nota_a_biblioteca(\n",
" conexion=conexion_admin,\n",
" biblioteca_id=\"BBLI20250511-a91dbb2168172979414\",\n",
" titulo=\"sajdhasjdhasjdh\"\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "431f24f1",
"metadata": {},
"outputs": [],
"source": [
"def listar_notas_de_biblioteca(conexion: PostgresConexion, biblioteca_id: str) -> list[dict]:\n",
" repo_biblioteca = BibliotecaRepo(conexion)\n",
" biblioteca = repo_biblioteca.get_by_id(biblioteca_id)\n",
" if not biblioteca:\n",
" raise ValueError(f\"No se encontró la biblioteca con ID: {biblioteca_id}\")\n",
"\n",
" metadata = MetaData()\n",
" tabla, ModeloNota = generar_tabla_nota_para_biblioteca(biblioteca.nombre, biblioteca.vector_dim, metadata)\n",
" metadata.create_all(conexion.get_engine())\n",
"\n",
" repo_nota = NotaRepo(conexion.get_session(), ModeloNota)\n",
" notas = repo_nota.get_all()\n",
" return [\n",
" {\n",
" \"id\": n.id,\n",
" \"titulo\": n.titulo,\n",
" \"tags\": n.tags,\n",
" \"texto\": n.texto,\n",
" \"resumen\": n.resumen,\n",
" \"conexiones\": n.conexiones\n",
" }\n",
" for n in notas\n",
" ]"
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "ae4f2994",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"E:\\Fitz_Studio\\src\\TextManager\\notas_biblioteca_mmr.py:51: SAWarning: This declarative base already contains a class with the same class name and module name as src.TextManager.notas_biblioteca_mmr.NotaModel, and will be replaced in the string-lookup table.\n",
" mapper_registry.map_imperatively(NotaModel, tabla)\n"
]
},
{
"ename": "TypeError",
"evalue": "'NoneType' object is not iterable",
"output_type": "error",
"traceback": [
"\u001b[31m---------------------------------------------------------------------------\u001b[39m",
"\u001b[31mTypeError\u001b[39m Traceback (most recent call last)",
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[14]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[43mlistar_notas_de_biblioteca\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 2\u001b[39m \u001b[43m \u001b[49m\u001b[43mconexion\u001b[49m\u001b[43m=\u001b[49m\u001b[43mconexion_admin\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 3\u001b[39m \u001b[43m \u001b[49m\u001b[43mbiblioteca_id\u001b[49m\u001b[43m=\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mBBLI20250511-a91dbb2168172979414\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\n\u001b[32m 4\u001b[39m \u001b[43m)\u001b[49m\n",
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[13]\u001b[39m\u001b[32m, line 12\u001b[39m, in \u001b[36mlistar_notas_de_biblioteca\u001b[39m\u001b[34m(conexion, biblioteca_id)\u001b[39m\n\u001b[32m 9\u001b[39m metadata.create_all(conexion.get_engine())\n\u001b[32m 11\u001b[39m repo_nota = NotaRepo(conexion.get_session(), ModeloNota)\n\u001b[32m---> \u001b[39m\u001b[32m12\u001b[39m notas = \u001b[43mrepo_nota\u001b[49m\u001b[43m.\u001b[49m\u001b[43mget_all\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 13\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m [\n\u001b[32m 14\u001b[39m {\n\u001b[32m 15\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mid\u001b[39m\u001b[33m\"\u001b[39m: n.id,\n\u001b[32m (...)\u001b[39m\u001b[32m 22\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m n \u001b[38;5;129;01min\u001b[39;00m notas\n\u001b[32m 23\u001b[39m ]\n",
"\u001b[36mFile \u001b[39m\u001b[32mE:\\Fitz_Studio\\src\\TextManager\\notas_biblioteca_mmr.py:109\u001b[39m, in \u001b[36mget_all\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 0\u001b[39m <Error retrieving source code with stack_data see ipython/ipython#13598>\n",
"\u001b[36mFile \u001b[39m\u001b[32mE:\\Fitz_Studio\\src\\TextManager\\notas_biblioteca_mmr.py:109\u001b[39m, in \u001b[36m<listcomp>\u001b[39m\u001b[34m(.0)\u001b[39m\n\u001b[32m 0\u001b[39m <Error retrieving source code with stack_data see ipython/ipython#13598>\n",
"\u001b[36mFile \u001b[39m\u001b[32mE:\\Fitz_Studio\\src\\TextManager\\notas_biblioteca_mmr.py:82\u001b[39m, in \u001b[36mfrom_model\u001b[39m\u001b[34m(model)\u001b[39m\n\u001b[32m 76\u001b[39m \u001b[38;5;129m@staticmethod\u001b[39m\n\u001b[32m 77\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mfrom_model\u001b[39m(model) -> Nota:\n\u001b[32m 78\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m Nota(\n\u001b[32m 79\u001b[39m \u001b[38;5;28mid\u001b[39m=model.id,\n\u001b[32m 80\u001b[39m titulo=model.titulo,\n\u001b[32m 81\u001b[39m tags=model.tags.split(\u001b[33m\"\u001b[39m\u001b[33m,\u001b[39m\u001b[33m\"\u001b[39m) \u001b[38;5;28;01mif\u001b[39;00m model.tags \u001b[38;5;28;01melse\u001b[39;00m [],\n\u001b[32m---> \u001b[39m\u001b[32m82\u001b[39m conexiones=model.conexiones.split(\u001b[33m\"\u001b[39m\u001b[33m,\u001b[39m\u001b[33m\"\u001b[39m) \u001b[38;5;28;01mif\u001b[39;00m model.conexiones \u001b[38;5;28;01melse\u001b[39;00m [],\n\u001b[32m 83\u001b[39m texto=model.texto,\n\u001b[32m 84\u001b[39m resumen=model.resumen,\n\u001b[32m 85\u001b[39m vector=\u001b[38;5;28mlist\u001b[39m(model.vector),\n\u001b[32m 86\u001b[39m vector_resumen=\u001b[38;5;28mlist\u001b[39m(model.vector_resumen) \u001b[38;5;28;01mif\u001b[39;00m model.vector_resumen \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;28;01melse\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m 87\u001b[39m )\n",
"\u001b[31mTypeError\u001b[39m: 'NoneType' object is not iterable"
]
}
],
"source": [
"listar_notas_de_biblioteca(\n",
" conexion=conexion_admin,\n",
" biblioteca_id=\"BBLI20250511-a91dbb2168172979414\"\n",
")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".venv",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.11"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
@@ -1,21 +1,99 @@
# backend/api/v1/biblioteca.py
from fastapi import APIRouter, Depends
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from typing import List, Optional
from fastapi import Path
from backend.db.conexion import get_conexion
from backend.services.text_manager import crear_biblioteca
from backend.services.text_manager import (
crear_biblioteca,
listar_bibliotecas,
agregar_nota_a_biblioteca,
listar_notas_de_biblioteca
)
from src.ConexionSql.Postgres_conexion import PostgresConexion
router = APIRouter()
# ---------------------------
# MODELOS PARA BIBLIOTECAS
# ---------------------------
class BibliotecaInput(BaseModel):
nombre_biblioteca: str
descripcion: str
@router.post("/")
@router.post("/", summary="Crear una nueva biblioteca")
def crear_biblioteca_endpoint(
data: BibliotecaInput,
conexion: PostgresConexion = Depends(get_conexion)
):
return crear_biblioteca(nombre_biblioteca=data.nombre_biblioteca,
descripcion=data.descripcion,
conexion=conexion)
try:
return crear_biblioteca(
nombre_biblioteca=data.nombre_biblioteca,
descripcion=data.descripcion,
conexion=conexion
)
except ValueError as e:
raise HTTPException(status_code=404, detail=str(e))
except Exception as e:
raise HTTPException(status_code=500, detail="Error interno al crear la biblioteca")
@router.get("/list", summary="Listar todas las bibliotecas")
def listar_todas_bibliotecas(
conexion: PostgresConexion = Depends(get_conexion)
):
try:
return listar_bibliotecas(conexion=conexion)
except ValueError as e:
raise HTTPException(status_code=404, detail=str(e))
except Exception as e:
raise HTTPException(status_code=500, detail="Error interno al listar las bibliotecas")
# ---------------------------
# MODELOS PARA NOTAS
# ---------------------------
class NotaInput(BaseModel):
titulo: str
texto: str = ""
tags: Optional[List[str]] = []
conexiones: Optional[List[str]] = []
resumen: Optional[str] = ""
@router.post("/nota/{biblioteca_id}", summary="Agregar una nota a una biblioteca")
def agregar_nota(
biblioteca_id: str = Path(..., description="ID de la biblioteca a la que se agregará la nota"),
nota: NotaInput = ..., # viene del body
conexion: PostgresConexion = Depends(get_conexion)
):
try:
return agregar_nota_a_biblioteca(
conexion=conexion,
biblioteca_id=biblioteca_id,
titulo=nota.titulo,
texto=nota.texto,
tags=nota.tags,
conexiones=nota.conexiones,
resumen=nota.resumen
)
except ValueError as e:
raise HTTPException(status_code=404, detail=str(e))
except Exception as e:
raise HTTPException(status_code=500, detail="Error interno al agregar la nota")
@router.get("/nota/list/{biblioteca_id}", summary="Listar todas las notas de una biblioteca")
def listar_notas(
biblioteca_id: str,
conexion: PostgresConexion = Depends(get_conexion)
):
try:
return listar_notas_de_biblioteca(conexion, biblioteca_id)
except ValueError as e:
raise HTTPException(status_code=404, detail=str(e))
except Exception as e:
raise HTTPException(status_code=500, detail="Error interno al listar las notas")
+106 -4
View File
@@ -3,8 +3,9 @@ from src.TextManager.biblioteca_mmr import BibliotecaRepo
from src.Llms.Embedders.Openai_embedder import OpenAIEmbedder
from src.ApiKeys.openai_apikey_mmr import OpenAICredencialRepo
from src.ConexionSql.Postgres_conexion import PostgresConexion
from backend.db.conexion import get_conexion
from src.TextManager.nota import Nota
from src.TextManager.notas_biblioteca_mmr import generar_tabla_nota_para_biblioteca, NotaRepo
from sqlalchemy import MetaData
def crear_biblioteca(nombre_biblioteca: str, conexion: PostgresConexion, descripcion: str):
cred_repo = OpenAICredencialRepo(conexion)
@@ -26,5 +27,106 @@ def crear_biblioteca(nombre_biblioteca: str, conexion: PostgresConexion, descrip
}
if __name__ == "__main__":
crear_biblioteca("hola_intento5")
def listar_bibliotecas(conexion: PostgresConexion) -> list[dict]:
repo = BibliotecaRepo(conexion)
bibliotecas: list[Biblioteca] = repo.get_all()
return [
{
"id": b.id,
"nombre": b.nombre,
"descripcion": b.descripcion,
"vector_dim": b.vector_dim
}
for b in bibliotecas
]
def agregar_nota_a_biblioteca(
conexion: PostgresConexion,
biblioteca_id: str,
titulo: str,
texto: str = "",
tags: list[str] = None,
conexiones: list[str] = None,
resumen: str = ""
):
print("[INFO] Iniciando el proceso de agregar nota a la biblioteca...")
# Obtener la biblioteca
print(f"[INFO] Buscando biblioteca con ID: {biblioteca_id}")
repo_biblioteca = BibliotecaRepo(conexion)
biblioteca = repo_biblioteca.get_by_id(biblioteca_id)
if biblioteca is None:
print(f"[ERROR] No se encontró la biblioteca con ID {biblioteca_id}")
raise ValueError(f"No se encontró la biblioteca con ID {biblioteca_id}")
print(f"[INFO] Biblioteca encontrada: {biblioteca.nombre} (vector_dim={biblioteca.vector_dim})")
# Crear objeto Nota
print(f"[INFO] Creando objeto Nota con título: {titulo}")
nota = Nota(
titulo=titulo,
texto=texto,
tags=tags or [],
conexiones=conexiones or [],
resumen=resumen or "",
# vector=biblioteca.embedder.embed_text(texto),
# vector_resumen=biblioteca.embedder.embed_text(resumen) if resumen else None
)
# Mostrar atributos seguros
print(
f"[DEBUG] Nota creada: titulo='{nota.titulo}', "
f"texto_len={len(nota.texto)}, "
f"tags={len(nota.tags)}, "
f"conexiones={len(nota.conexiones)}, "
f"resumen_len={len(nota.resumen)}"
)
# Preparar tabla y modelo de nota
print(f"[INFO] Generando tabla y modelo de Nota para la biblioteca: {biblioteca.nombre}")
metadata = MetaData()
tabla, ModeloNota = generar_tabla_nota_para_biblioteca(
biblioteca.nombre,
biblioteca.vector_dim,
metadata
)
print(f"[INFO] Creando tabla en la base de datos si no existe...")
metadata.create_all(conexion.get_engine())
print(f"[INFO] Tabla '{tabla.name}' verificada/creada.")
# Guardar la nota
print(f"[INFO] Guardando nota en la base de datos...")
repo_nota = NotaRepo(conexion.get_session(), ModeloNota)
nota_id = repo_nota.add(nota)
print(f"[INFO] Nota guardada con ID: {nota_id}")
resultado = {
"mensaje": f"Nota '{titulo}' agregada con éxito a la biblioteca '{biblioteca.nombre}'.",
"nota_id": nota_id
}
print(f"[SUCCESS] {resultado['mensaje']}")
return resultado
def listar_notas_de_biblioteca(conexion: PostgresConexion, biblioteca_id: str) -> list[dict]:
repo_biblioteca = BibliotecaRepo(conexion)
biblioteca = repo_biblioteca.get_by_id(biblioteca_id)
if not biblioteca:
raise ValueError(f"No se encontró la biblioteca con ID: {biblioteca_id}")
metadata = MetaData()
tabla, ModeloNota = generar_tabla_nota_para_biblioteca(biblioteca.nombre, biblioteca.vector_dim, metadata)
metadata.create_all(conexion.get_engine())
repo_nota = NotaRepo(conexion.get_session(), ModeloNota)
notas = repo_nota.get_all()
return [
{
"id": n.id,
"titulo": n.titulo,
"tags": n.tags,
"texto": n.texto,
"resumen": n.resumen,
"conexiones": n.conexiones
}
for n in notas
]
+101 -14
View File
@@ -13,6 +13,7 @@
"@react-three/fiber": "^9.1.2",
"@tabler/icons": "^3.31.0",
"@tabler/icons-react": "^3.31.0",
"axios": "^1.9.0",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-rnd": "^10.5.2",
@@ -3304,6 +3305,12 @@
"node": ">= 0.4"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"license": "MIT"
},
"node_modules/available-typed-arrays": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
@@ -3330,6 +3337,17 @@
"node": ">=4"
}
},
"node_modules/axios": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz",
"integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/axobject-query": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
@@ -3552,7 +3570,6 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
@@ -3713,6 +3730,18 @@
"dev": true,
"license": "MIT"
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"license": "MIT",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -4053,6 +4082,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
@@ -4127,7 +4165,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"dev": true,
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
@@ -4279,7 +4316,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -4289,7 +4325,6 @@
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -4334,7 +4369,6 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"dev": true,
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
@@ -4347,7 +4381,6 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"dev": true,
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
@@ -5061,6 +5094,26 @@
"dev": true,
"license": "ISC"
},
"node_modules/follow-redirects": {
"version": "1.15.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"license": "MIT",
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/for-each": {
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
@@ -5094,6 +5147,42 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/form-data": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
"integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/form-data/node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/form-data/node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@@ -5133,7 +5222,6 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -5184,7 +5272,6 @@
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
@@ -5218,7 +5305,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"dev": true,
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
@@ -5413,7 +5499,6 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -5492,7 +5577,6 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -5505,7 +5589,6 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"dev": true,
"license": "MIT",
"dependencies": {
"has-symbols": "^1.0.3"
@@ -5521,7 +5604,6 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
@@ -6579,7 +6661,6 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -7556,6 +7637,12 @@
"node": ">= 0.10"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT"
},
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+1
View File
@@ -25,6 +25,7 @@
"@react-three/fiber": "^9.1.2",
"@tabler/icons": "^3.31.0",
"@tabler/icons-react": "^3.31.0",
"axios": "^1.9.0",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-rnd": "^10.5.2",
+5 -1
View File
@@ -3,6 +3,7 @@ import { HomePage } from './pages/Home.page';
import { Consulta_API } from './pages/Consulta_api';
import { Error_404 } from './pages/404'; // Ajusta si está en otra carpeta
import { Grid_Dashboard } from './pages/Grid_dashboard'; // Ajusta si está en otra carpeta
import { Biblioteca } from './pages/Biblioteca';
const router = createBrowserRouter([
{
@@ -17,7 +18,10 @@ const router = createBrowserRouter([
path: '/Grid_Dashboard',
element: <Grid_Dashboard />,
},
{
path: '/Biblioteca',
element: <Biblioteca />,
},
{
+1
View File
@@ -4,6 +4,7 @@ export const submenuLinks = {
Home: [
{ label: 'Inicio', to: '/' },
{ label: 'Consulta Api', to: '/Consulta_API' },
{ label: 'Biblioteca', to: '/Biblioteca' },
],
Dashboard: [
+169
View File
@@ -0,0 +1,169 @@
import { useEffect, useState } from 'react';
import {
AppShell,
Stack,
Card,
Text,
Title,
ScrollArea,
Group,
Button,
TextInput,
Modal,
Box,
Loader,
} from '@mantine/core';
import { AppShellWithMenu } from '../components/Appshell/Appshell';
import axios from 'axios';
type Nota = {
id: string;
titulo: string;
texto: string;
};
type Biblioteca = {
id: string;
nombre: string;
descripcion: string;
notas: Nota[];
};
export function Biblioteca() {
const [bibliotecas, setBibliotecas] = useState<Biblioteca[]>([]);
const [bibliotecaSeleccionada, setBibliotecaSeleccionada] = useState<Biblioteca | null>(null);
const [modalAbierto, setModalAbierto] = useState(false);
const [tituloNota, setTituloNota] = useState('');
const [contenidoNota, setContenidoNota] = useState('');
const [loadingNotas, setLoadingNotas] = useState(false);
const fetchBibliotecas = async () => {
try {
const res = await axios.get('/api/v1/text_manager/list');
console.log('📦 Respuesta del backend:', res.data);
if (!Array.isArray(res.data)) {
console.error('❌ La respuesta no es un array:', res.data);
return;
}
const bibliotecasConNotas = await Promise.all(
res.data.map(async (biblio: Omit<Biblioteca, 'notas'>) => {
const notas = await axios.get(`/api/v1/text_manager/nota/list/${biblio.id}`);
return { ...biblio, notas: notas.data as Nota[] };
})
);
setBibliotecas(bibliotecasConNotas);
setBibliotecaSeleccionada(bibliotecasConNotas[0] || null);
} catch (error) {
console.error('Error al cargar bibliotecas:', error);
}
};
const agregarNota = async () => {
if (!bibliotecaSeleccionada) return;
try {
await axios.post(`/api/v1/text_manager/nota/${bibliotecaSeleccionada.id}`, {
titulo: tituloNota,
texto: contenidoNota,
tags: [],
conexiones: [],
resumen: '',
});
setLoadingNotas(true);
const nuevasNotas = await axios.get(`/api/v1/text_manager/nota/list/${bibliotecaSeleccionada.id}`);
const nuevasBibliotecas = bibliotecas.map((b) =>
b.id === bibliotecaSeleccionada.id ? { ...b, notas: nuevasNotas.data as Nota[] } : b
);
setBibliotecas(nuevasBibliotecas);
setBibliotecaSeleccionada(nuevasBibliotecas.find((b) => b.id === bibliotecaSeleccionada.id) || null);
setTituloNota('');
setContenidoNota('');
setModalAbierto(false);
} catch (error) {
console.error('Error al agregar nota:', error);
} finally {
setLoadingNotas(false);
}
};
useEffect(() => {
fetchBibliotecas();
}, []);
return (
<AppShellWithMenu>
<Box display="flex" h="100%">
<Box w={240} p="md">
<ScrollArea h="100%">
<Stack>
{bibliotecas.map((biblio) => (
<Button
key={biblio.id}
size="xs"
fullWidth
variant={biblio.id === bibliotecaSeleccionada?.id ? 'filled' : 'light'}
color="blue"
onClick={() => setBibliotecaSeleccionada(biblio)}
>
{biblio.nombre}
</Button>
))}
</Stack>
</ScrollArea>
</Box>
<Box p="md" style={{ flex: 1 }}>
{bibliotecaSeleccionada ? (
<Stack>
<Title order={2}>{bibliotecaSeleccionada.nombre}</Title>
<Group>
<Button color="teal" onClick={fetchBibliotecas}>
🔄 Recuperar bibliotecas
</Button>
<Button onClick={() => setModalAbierto(true)}>Agregar nota</Button>
</Group>
<Group>
{loadingNotas ? (
<Loader />
) : (
bibliotecaSeleccionada.notas.map((nota) => (
<Card key={nota.id} shadow="sm" padding="lg" radius="md" withBorder style={{ width: 300 }}>
<Title order={4}>{nota.titulo}</Title>
<Text>{nota.texto}</Text>
</Card>
))
)}
</Group>
</Stack>
) : (
<Stack>
<Text>Selecciona una biblioteca</Text>
<Button color="teal" onClick={fetchBibliotecas}>
🔄 Recuperar bibliotecas
</Button>
</Stack>
)}
</Box>
</Box>
<Modal opened={modalAbierto} onClose={() => setModalAbierto(false)} title="Agregar nueva nota">
<Stack>
<TextInput
label="Título"
value={tituloNota}
onChange={(event) => setTituloNota(event.currentTarget.value)}
/>
<TextInput
label="Contenido"
value={contenidoNota}
onChange={(event) => setContenidoNota(event.currentTarget.value)}
/>
<Button onClick={agregarNota}>Guardar</Button>
</Stack>
</Modal>
</AppShellWithMenu>
);
}
+9
View File
@@ -5,6 +5,15 @@ import svgr from 'vite-plugin-svgr';
export default defineConfig({
plugins: [react(), tsconfigPaths(), svgr()],
server: {
proxy: {
'/api': {
target: 'http://localhost:8000',
changeOrigin: true,
secure: false,
},
},
},
test: {
globals: true,
environment: 'jsdom',
+58
View File
@@ -1224,6 +1224,11 @@ async-function@^1.0.0:
resolved "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz"
integrity sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz"
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
available-typed-arrays@^1.0.7:
version "1.0.7"
resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz"
@@ -1236,6 +1241,15 @@ axe-core@^4.10.0:
resolved "https://registry.npmjs.org/axe-core/-/axe-core-4.10.3.tgz"
integrity sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==
axios@^1.9.0:
version "1.9.0"
resolved "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz"
integrity sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==
dependencies:
follow-redirects "^1.15.6"
form-data "^4.0.0"
proxy-from-env "^1.1.0"
axobject-query@^4.1.0:
version "4.1.0"
resolved "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz"
@@ -1446,6 +1460,13 @@ colord@^2.9.3:
resolved "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz"
integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==
combined-stream@^1.0.8:
version "1.0.8"
resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
dependencies:
delayed-stream "~1.0.0"
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
@@ -1641,6 +1662,11 @@ define-properties@^1.1.3, define-properties@^1.2.1:
has-property-descriptors "^1.0.0"
object-keys "^1.1.1"
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz"
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
depd@^2.0.0, depd@2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz"
@@ -2273,6 +2299,11 @@ flatted@^3.2.9, flatted@^3.3.3:
resolved "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz"
integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==
follow-redirects@^1.15.6:
version "1.15.9"
resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz"
integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==
for-each@^0.3.3, for-each@^0.3.5:
version "0.3.5"
resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz"
@@ -2288,6 +2319,16 @@ foreground-child@^3.1.0:
cross-spawn "^7.0.6"
signal-exit "^4.0.1"
form-data@^4.0.0:
version "4.0.2"
resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz"
integrity sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
es-set-tostringtag "^2.1.0"
mime-types "^2.1.12"
forwarded@0.2.0:
version "0.2.0"
resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz"
@@ -3162,6 +3203,18 @@ mime-db@^1.54.0:
resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz"
integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==
mime-db@1.52.0:
version "1.52.0"
resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
mime-types@^2.1.12:
version "2.1.35"
resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
dependencies:
mime-db "1.52.0"
mime-types@^3.0.0, mime-types@^3.0.1:
version "3.0.1"
resolved "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz"
@@ -3586,6 +3639,11 @@ proxy-addr@^2.0.7:
forwarded "0.2.0"
ipaddr.js "1.9.1"
proxy-from-env@^1.1.0:
version "1.1.0"
resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
punycode@^2.1.0, punycode@^2.3.1:
version "2.3.1"
resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz"
+12 -8
View File
@@ -4,6 +4,8 @@ from typing import List, Optional
from src.ConexionSql.Base_conexion import ConexionBase
from sqlalchemy import MetaData # Asegúrate de importar esto
from src.TextManager.notas_biblioteca_mmr import generar_tabla_nota_para_biblioteca # Ajusta si es necesario
from sqlalchemy import inspect
class Biblioteca:
def __init__(
@@ -24,7 +26,7 @@ class Biblioteca:
:param vector_dim: Dimensión del vector si no se proporciona un embedder.
"""
self.id = id if id is not None else GeneradorIDUnico("BBLI").generar()
self.nombre = nombre
self.nombre = nombre if "biblio" in nombre else f"biblio_{nombre}"
self.descripcion = descripcion
self.embedder = embedder
@@ -38,14 +40,16 @@ class Biblioteca:
def generar_modelo_notas(self, conexion: ConexionBase):
"""
Genera dinámicamente un modelo de notas asociado a esta biblioteca y lo crea en la base de datos.
:param conexion: Objeto de conexión a la base de datos.
:return: Clase del modelo SQLAlchemy correspondiente a las notas.
Previene la creación si la tabla ya existe.
"""
nombre_tabla = f"biblio_{self.nombre}"
metadata = MetaData()
nombre_tabla = f"{self.nombre}"
engine = conexion.get_engine()
inspector = inspect(engine)
if inspector.has_table(nombre_tabla):
raise ValueError(f"Ya existe una tabla con el nombre '{nombre_tabla}' en la base de datos.")
metadata = MetaData()
tabla, NotaModel = generar_tabla_nota_para_biblioteca(nombre_tabla, self.vector_dim, metadata)
metadata.create_all(engine)
return NotaModel
return NotaModel
+7 -2
View File
@@ -26,8 +26,8 @@ if pssword is None:
class BibliotecaModel(Base):
__tablename__ = "bibliotecas"
id = Column(String, primary_key=True)
nombre = Column(String, nullable=False)
id = Column(String, primary_key=True, unique=True)
nombre = Column(String, nullable=False, unique=True)
descripcion = Column(String, default="")
vector_dim = Column(Integer, nullable=False)
embedder_info = Column(String, nullable=True) # Se puede guardar nombre de clase o config encriptada
@@ -77,6 +77,11 @@ class BibliotecaRepo:
self.session = conexion.get_session()
def add(self, biblioteca: Biblioteca) -> str:
# Verificar si ya existe una biblioteca con el mismo nombre
existente = self.session.query(BibliotecaModel).filter_by(nombre=biblioteca.nombre).first()
if existente:
raise ValueError(f"Ya existe una biblioteca con el nombre '{biblioteca.nombre}'")
data = BibliotecaMapper.to_dict(biblioteca)
model = BibliotecaModel(**data)
self.session.add(model)
+10 -5
View File
@@ -33,8 +33,8 @@ def generar_tabla_nota_para_biblioteca(biblioteca_nombre: str, vector_dim: int,
Column("conexiones", String),
Column("texto", String),
Column("resumen", String),
Column("vector", Vector(vector_dim), nullable=False),
Column("vector_resumen", Vector(vector_dim), nullable=True), # AHORA ES OPCIONAL
Column("vector", Vector(vector_dim), nullable=True),
Column("vector_resumen", Vector(vector_dim), nullable=True),
)
class NotaModel:
@@ -46,9 +46,12 @@ def generar_tabla_nota_para_biblioteca(biblioteca_nombre: str, vector_dim: int,
self.texto = nota.texto
self.resumen = nota.resumen
self.vector = nota.vector
self.vector_resumen = getattr(nota, "vector_resumen", None) # AHORA ES OPCIONAL
self.vector_resumen = getattr(nota, "vector_resumen", None)
# Evitar mapear dos veces la misma clase
if NotaModel not in mapper_registry._class_registry.values():
mapper_registry.map_imperatively(NotaModel, tabla)
mapper_registry.map_imperatively(NotaModel, tabla)
return tabla, NotaModel
@@ -79,7 +82,7 @@ class NotaMapper:
conexiones=model.conexiones.split(",") if model.conexiones else [],
texto=model.texto,
resumen=model.resumen,
vector=list(model.vector),
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
)
@@ -89,6 +92,8 @@ class NotaMapper:
class NotaRepo:
def __init__(self, session: Session, modelo_nota):
if modelo_nota is None:
raise ValueError("No se puede instanciar NotaRepo sin un modelo válido de nota.")
self.session = session
self.ModeloNota = modelo_nota