frontend añadido y backend de creacion de notas
This commit is contained in:
@@ -0,0 +1,627 @@
|
||||
import marimo
|
||||
|
||||
__generated_with = "0.15.5"
|
||||
app = marimo.App(width="columns")
|
||||
|
||||
|
||||
@app.cell(column=0)
|
||||
def _():
|
||||
import marimo as mo
|
||||
return (mo,)
|
||||
|
||||
|
||||
@app.cell
|
||||
def _():
|
||||
PUERTO_POSTGRES = 55455
|
||||
SERVICIO = "postgres_ext"
|
||||
DB_PASSWORD = "mipassword"
|
||||
DB_USER = "postgres"
|
||||
DB_NAME = "basededatos"
|
||||
DB_HOST = "localhost"
|
||||
return DB_HOST, DB_NAME, DB_PASSWORD, DB_USER, PUERTO_POSTGRES
|
||||
|
||||
|
||||
@app.cell
|
||||
def _(DB_HOST, DB_NAME, DB_PASSWORD, DB_USER, PUERTO_POSTGRES):
|
||||
# scaffold_backend.py
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
BASE_DIR = Path.cwd() / "backend"
|
||||
DB_DIR = BASE_DIR / "db"
|
||||
DB_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# __init__.py
|
||||
(DB_DIR / "__init__.py").write_text("", encoding="utf-8")
|
||||
|
||||
# base.py
|
||||
base_py = """\
|
||||
# backend/db/base.py
|
||||
from sqlalchemy.orm import declarative_base
|
||||
|
||||
# Declarative Base común para todos los modelos ORM
|
||||
Base = declarative_base()
|
||||
"""
|
||||
(DB_DIR / "base.py").write_text(base_py, encoding="utf-8")
|
||||
|
||||
# session.py
|
||||
session_py = f"""\
|
||||
# backend/db/session.py
|
||||
import os
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from urllib.parse import quote_plus
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Cargar variables de entorno (.env) desde backend/.env
|
||||
load_dotenv(dotenv_path=os.path.join(os.path.dirname(__file__), "..", ".env"))
|
||||
|
||||
DB_USER = os.getenv("DB_USER")
|
||||
DB_PASSWORD = quote_plus(os.getenv("DB_PASSWORD"))
|
||||
DB_HOST = os.getenv("DB_HOST")
|
||||
DB_PORT = os.getenv("DB_PORT", "{PUERTO_POSTGRES}")
|
||||
DB_NAME = os.getenv("DB_NAME")
|
||||
|
||||
DATABASE_URL = (
|
||||
f"postgresql+psycopg2://{{DB_USER}}:{{DB_PASSWORD}}@{{DB_HOST}}:{{DB_PORT}}/{{DB_NAME}}"
|
||||
)
|
||||
|
||||
# Crear engine
|
||||
engine = create_engine(
|
||||
DATABASE_URL,
|
||||
pool_size=5,
|
||||
max_overflow=10,
|
||||
future=True,
|
||||
echo=False, # pon True para ver las queries en consola
|
||||
)
|
||||
|
||||
# Fábrica de sesiones
|
||||
SessionLocal = sessionmaker(
|
||||
bind=engine,
|
||||
autoflush=False,
|
||||
expire_on_commit=False,
|
||||
future=True,
|
||||
)
|
||||
"""
|
||||
(DB_DIR / "session.py").write_text(session_py, encoding="utf-8")
|
||||
|
||||
# .env handling
|
||||
env_file = BASE_DIR / ".env"
|
||||
default_env = {
|
||||
"DB_USER": DB_USER,
|
||||
"DB_PASSWORD": DB_PASSWORD,
|
||||
"DB_HOST": DB_HOST,
|
||||
"DB_PORT": PUERTO_POSTGRES,
|
||||
"DB_NAME": DB_NAME,
|
||||
}
|
||||
|
||||
|
||||
if not env_file.exists():
|
||||
# Crear archivo nuevo con todas las claves
|
||||
env_content = "\n".join([f"{k}={v}" for k, v in default_env.items()]) + "\n"
|
||||
env_file.write_text(env_content, encoding="utf-8")
|
||||
print(f"✅ Creado {env_file}")
|
||||
else:
|
||||
# Leer y comprobar claves
|
||||
existing = env_file.read_text(encoding="utf-8").splitlines()
|
||||
existing_keys = {line.split("=")[0] for line in existing if "=" in line}
|
||||
missing = {k: v for k, v in default_env.items() if k not in existing_keys}
|
||||
if missing:
|
||||
with env_file.open("a", encoding="utf-8") as f:
|
||||
for k, v in missing.items():
|
||||
f.write(f"{k}={v}\n")
|
||||
print(f"📝 Añadidas claves faltantes: {', '.join(missing.keys())}")
|
||||
else:
|
||||
print(f"ℹ️ {env_file} ya tiene todas las claves necesarias.")
|
||||
|
||||
print(f"✅ Creado {DB_DIR/'base.py'}")
|
||||
print(f"✅ Creado {DB_DIR/'session.py'}")
|
||||
return Path, os
|
||||
|
||||
|
||||
@app.cell
|
||||
def _(Path, os):
|
||||
|
||||
# import os
|
||||
|
||||
RUTA_CARPETA_SNIPPETS = os.environ["SNIPPETS_ROUTE"]
|
||||
|
||||
DEST_BASE = Path("./domains/arquitecture_layer")
|
||||
DEST_BASE.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
archivos = {
|
||||
"utils/ArquitectureLayer/Repo.py": "Repo.py",
|
||||
"utils/ArquitectureLayer/Mapper.py": "Mapper.py",
|
||||
"utils/ArquitectureLayer/Model.py": "Model.py",
|
||||
}
|
||||
|
||||
# Copiar los archivos
|
||||
for origen_rel, nombre_dest in archivos.items():
|
||||
origen = Path(RUTA_CARPETA_SNIPPETS) / origen_rel
|
||||
destino = DEST_BASE / nombre_dest
|
||||
destino.write_text(origen.read_text(encoding="utf-8"), encoding="utf-8")
|
||||
print(f"✅ {origen} → {destino}")
|
||||
|
||||
# Crear __init__.py para exponer los módulos
|
||||
init_file = DEST_BASE / "__init__.py"
|
||||
init_code = """\
|
||||
# domains/arquitecture_layer/__init__.py
|
||||
from .Repo import *
|
||||
from .Mapper import *
|
||||
from .Model import *
|
||||
|
||||
__all__ = ["Repo", "Mapper", "Model"]
|
||||
"""
|
||||
init_file.write_text(init_code, encoding="utf-8")
|
||||
print(f"✅ Creado {init_file}")
|
||||
return (RUTA_CARPETA_SNIPPETS,)
|
||||
|
||||
|
||||
@app.cell(column=1)
|
||||
def _(os):
|
||||
#Obtenemos las variables
|
||||
|
||||
# import os
|
||||
|
||||
def obtener_texto_archivo(ruta_archivo):
|
||||
"""Lee un archivo o todos los archivos de un directorio."""
|
||||
if os.path.isdir(ruta_archivo):
|
||||
textos = {}
|
||||
for archivo in os.listdir(ruta_archivo):
|
||||
ruta = os.path.join(ruta_archivo, archivo)
|
||||
if os.path.isfile(ruta):
|
||||
with open(ruta, 'r', encoding='utf-8') as f:
|
||||
textos[archivo] = f.read()
|
||||
return textos
|
||||
elif os.path.isfile(ruta_archivo):
|
||||
with open(ruta_archivo, 'r', encoding='utf-8') as f:
|
||||
return f.read()
|
||||
else:
|
||||
raise FileNotFoundError(f"No se encontró archivo o directorio: {ruta_archivo}")
|
||||
|
||||
|
||||
|
||||
repo_code = obtener_texto_archivo("domains/arquitecture_layer/Repo.py")
|
||||
mapper_code = obtener_texto_archivo("domains/arquitecture_layer/Mapper.py")
|
||||
model_code = obtener_texto_archivo("domains/arquitecture_layer/Model.py")
|
||||
|
||||
repo_ruta_code = "from domains.arquitecture_layer.Model import Model_base"
|
||||
mapper_ruta_code = "domains.arquitecture_layer.Mapper import Mapper_base"
|
||||
model_ruta_code = "domains.arquitecture_layer.Repo import Repo_base"
|
||||
return (
|
||||
mapper_code,
|
||||
mapper_ruta_code,
|
||||
model_code,
|
||||
model_ruta_code,
|
||||
repo_code,
|
||||
repo_ruta_code,
|
||||
)
|
||||
|
||||
|
||||
@app.cell
|
||||
def _(mo):
|
||||
input_entidad = mo.ui.text_area("", label="Entidad a crear", rows=1, full_width=True)
|
||||
input_descripcion = mo.ui.text_area("", label="Descripcion de la Entidad", rows=3, full_width=True)
|
||||
|
||||
mo.vstack([input_entidad, input_descripcion])
|
||||
return input_descripcion, input_entidad
|
||||
|
||||
|
||||
@app.cell
|
||||
def _(mo):
|
||||
esquema = mo.ui.text(
|
||||
value="public",
|
||||
label="Esquema:"
|
||||
)
|
||||
esquema
|
||||
return (esquema,)
|
||||
|
||||
|
||||
@app.cell
|
||||
def _(
|
||||
RUTA_CARPETA_SNIPPETS,
|
||||
esquema,
|
||||
input_descripcion,
|
||||
input_entidad,
|
||||
mo,
|
||||
os,
|
||||
):
|
||||
from jinja2 import Template
|
||||
# from pathlib import Path
|
||||
|
||||
# RUTA_CARPETA_SNIPPETS = os.environ["SNIPPETS_ROUTE"]
|
||||
|
||||
with open(os.path.join(RUTA_CARPETA_SNIPPETS, "markdown/plantilla_generacion_campos_detalles.md"), "r", encoding="utf-8") as _f:
|
||||
plantilla_detalles = Template(_f.read())
|
||||
|
||||
entidad = input_entidad.value.lower()
|
||||
descripcion = input_descripcion.value.lower()
|
||||
|
||||
_contexto = {
|
||||
|
||||
"ENTIDAD" : entidad, # Nombre de la entidad
|
||||
"DESCRIPCION" : descripcion,
|
||||
"SCHEMA" : esquema.value
|
||||
}
|
||||
|
||||
# Pasar el diccionario con **
|
||||
resultado_plantilla = plantilla_detalles.render(**_contexto)
|
||||
|
||||
mo.output.append(mo.md("### Prompt para la base del modelo:"))
|
||||
mo.output.append(mo.md(resultado_plantilla))
|
||||
return Template, entidad, resultado_plantilla
|
||||
|
||||
|
||||
@app.cell
|
||||
def _(mo):
|
||||
run_button = mo.ui.run_button(label="Generar JSON", kind="neutral")
|
||||
run_button
|
||||
return (run_button,)
|
||||
|
||||
|
||||
@app.cell
|
||||
def _(mo, os, resultado_plantilla, run_button):
|
||||
from openai import OpenAI
|
||||
import json
|
||||
|
||||
|
||||
def call_openai_to_json(template_text: str) -> dict:
|
||||
"""Call OpenAI and return parsed JSON; raises on errors."""
|
||||
# NOTE: Read API key from environment for reproducibility
|
||||
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
|
||||
resp = client.chat.completions.create(
|
||||
model="gpt-4o", # or "gpt-5" if available
|
||||
response_format={"type": "json_object"},
|
||||
messages=[
|
||||
{"role": "system", "content": "Eres un asistente estricto que SIEMPRE devuelve JSON válido y nada más."},
|
||||
{"role": "user", "content": resultado_plantilla}
|
||||
],
|
||||
temperature=0.2
|
||||
)
|
||||
json_text = resp.choices[0].message.content
|
||||
return json.loads(json_text)
|
||||
|
||||
|
||||
status_message = "Pulsa el botón para generar."
|
||||
json_text = None # Default sentinel so dependents can run
|
||||
|
||||
if run_button.value:
|
||||
try:
|
||||
json_text = call_openai_to_json(resultado_plantilla)
|
||||
status_message = "JSON generado correctamente."
|
||||
except Exception as exc:
|
||||
# Safe error surface without stopping the graph
|
||||
status_message = f"Error al generar: {exc!s}"
|
||||
|
||||
# Return a UI element; if no resultado, mostramos solo el mensaje
|
||||
mo.vstack([
|
||||
mo.md(f"**Estado:** {status_message}"),
|
||||
mo.json(json_text) if json_text is not None else mo.md("")
|
||||
])
|
||||
return OpenAI, json, json_text
|
||||
|
||||
|
||||
@app.cell
|
||||
def _(json, json_text, mo):
|
||||
from glom import glom, Coalesce
|
||||
|
||||
# --- 1) Normalizar entrada: str JSON -> dict
|
||||
def to_obj(x):
|
||||
if isinstance(x, str):
|
||||
return json.loads(x) # <- parsea el texto JSON
|
||||
return x
|
||||
|
||||
data = to_obj(json_text) # json_text puede ser str o dict
|
||||
|
||||
# Extraer listas
|
||||
campos_list = glom(data, Coalesce("CAMPOS", default=[]))
|
||||
relaciones_list = glom(data, Coalesce("RELACIONES", default=[]))
|
||||
indices_list = glom(data, Coalesce("INDEX_LIST", default=[]))
|
||||
unicos_list = glom(data, Coalesce("UNIQUE_LIST", default=[]))
|
||||
|
||||
# Por si vienen como string en lugar de lista:
|
||||
def ensure_list(v):
|
||||
if v is None:
|
||||
return []
|
||||
if isinstance(v, list):
|
||||
return v
|
||||
return [str(v)]
|
||||
|
||||
# Convertir a texto multilínea
|
||||
campos_list = ensure_list(campos_list)
|
||||
relaciones_list = ensure_list(relaciones_list)
|
||||
indices_list = ensure_list(indices_list)
|
||||
unicos_list = ensure_list(unicos_list)
|
||||
|
||||
campos = "\n".join(map(str, campos_list))
|
||||
relaciones = "\n".join(map(str, relaciones_list))
|
||||
indices = "\n".join(map(str, indices_list))
|
||||
unicos = "\n".join(map(str, unicos_list))
|
||||
|
||||
campos_final = mo.ui.text_area(campos, label="Campos", rows=15, full_width=True)
|
||||
relaciones_final = mo.ui.text_area(relaciones, label="Relaciones", rows=3, full_width=True)
|
||||
indices_final = mo.ui.text_area(indices, label="Indices", rows=3, full_width=True)
|
||||
unicos_final = mo.ui.text_area(unicos, label="Unicos", rows=3, full_width=True)
|
||||
|
||||
layout = mo.hstack(
|
||||
[
|
||||
campos_final,
|
||||
mo.vstack([relaciones_final, indices_final, unicos_final])
|
||||
],
|
||||
widths=[2, 1], # t1 ocupa el doble de ancho que el stack vertical
|
||||
gap=1 # margen entre los elementos en rem (1 rem ≈ 16 px)
|
||||
)
|
||||
|
||||
layout
|
||||
return campos_final, indices_final, relaciones_final, unicos_final
|
||||
|
||||
|
||||
@app.cell
|
||||
def _(
|
||||
RUTA_CARPETA_SNIPPETS,
|
||||
Template,
|
||||
campos_final,
|
||||
entidad,
|
||||
esquema,
|
||||
indices_final,
|
||||
mapper_code,
|
||||
mapper_ruta_code,
|
||||
mo,
|
||||
model_code,
|
||||
model_ruta_code,
|
||||
os,
|
||||
relaciones_final,
|
||||
repo_code,
|
||||
repo_ruta_code,
|
||||
unicos_final,
|
||||
):
|
||||
with open(os.path.join(RUTA_CARPETA_SNIPPETS, "markdown/plantilla_generacion_mmr.md"), "r", encoding="utf-8") as _f:
|
||||
plantilla = Template(_f.read())
|
||||
|
||||
with open(os.path.join(RUTA_CARPETA_SNIPPETS, "markdown/ejemplos_base_mmr.md"), "r", encoding="utf-8") as _f:
|
||||
ejemplo = _f.read()
|
||||
|
||||
# Diccionario con variables
|
||||
_contexto = {
|
||||
|
||||
"ENTIDAD" : entidad, # Nombre de la entidad
|
||||
|
||||
"RUTA_REPO_BASE": repo_ruta_code,
|
||||
"RUTA_MAPPER_BASE": mapper_ruta_code,
|
||||
"RUTA_MODEL_BASE": model_ruta_code,
|
||||
|
||||
"REPO_BASE": repo_code,
|
||||
"MAPPER_BASE": mapper_code,
|
||||
"MODEL_BASE": model_code,
|
||||
|
||||
"EJEMPLO": ejemplo,
|
||||
|
||||
"SCHEMA": esquema.value,
|
||||
"TABLA": entidad,
|
||||
|
||||
|
||||
"CAMPOS": campos_final.value,
|
||||
"RELACIONES" : relaciones_final.value,
|
||||
"INDEX_LIST": indices_final.value,
|
||||
"UNIQUE_LIST": unicos_final.value,
|
||||
|
||||
}
|
||||
|
||||
|
||||
# Pasar el diccionario con **
|
||||
resultado = plantilla.render(**_contexto)
|
||||
|
||||
mo.output.append(mo.md("### Prompt para el modelo:"))
|
||||
mo.output.append(mo.md(resultado))
|
||||
return (resultado,)
|
||||
|
||||
|
||||
@app.cell
|
||||
def _(mo):
|
||||
run_button_code = mo.ui.run_button(label="Generar código Python", kind="neutral")
|
||||
run_button_code
|
||||
return (run_button_code,)
|
||||
|
||||
|
||||
@app.cell
|
||||
def _(OpenAI, mo, os, resultado, run_button_code):
|
||||
# from openai import OpenAI
|
||||
|
||||
def call_openai_to_code(user_prompt: str) -> str:
|
||||
"""Call OpenAI and return Python code as plain text."""
|
||||
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
|
||||
resp = client.chat.completions.create(
|
||||
model="gpt-4o", # o "gpt-5" si lo tienes
|
||||
messages=[
|
||||
{
|
||||
"role": "system",
|
||||
"content": "Eres un asistente estricto que SIEMPRE devuelve codigo python de acuerdo a sus reglas"
|
||||
},
|
||||
{"role": "user", "content": user_prompt}
|
||||
],
|
||||
temperature=0.2
|
||||
)
|
||||
return resp.choices[0].message.content
|
||||
|
||||
_status_message = "Pulsa el botón para generar."
|
||||
respuesta_codigo = None # valor por defecto
|
||||
|
||||
if run_button_code.value:
|
||||
try:
|
||||
respuesta_codigo = call_openai_to_code(resultado)
|
||||
_status_message = "Código generado correctamente."
|
||||
except Exception as exc:
|
||||
_status_message = f"Error al generar: {exc!s}"
|
||||
|
||||
mo.vstack([
|
||||
mo.md(f"**Estado:** {_status_message}"),
|
||||
mo.md(f"```python\n{respuesta_codigo}\n```") if respuesta_codigo else mo.md("")
|
||||
])
|
||||
return (respuesta_codigo,)
|
||||
|
||||
|
||||
@app.cell
|
||||
def _(entidad, mo, os, respuesta_codigo):
|
||||
# Botón para guardar
|
||||
boton_guardar = mo.ui.button(
|
||||
label="Guardar archivo",
|
||||
on_click=lambda _: guardar()
|
||||
)
|
||||
|
||||
def guardar():
|
||||
codigo_limpio = (
|
||||
respuesta_codigo.strip()
|
||||
.removeprefix("```python")
|
||||
.removesuffix("```")
|
||||
.strip()
|
||||
)
|
||||
|
||||
# Carpeta relativa (ej: ./domains dentro de tu proyecto)
|
||||
carpeta = "domains"
|
||||
os.makedirs(carpeta, exist_ok=True)
|
||||
|
||||
ruta = os.path.join(carpeta, f"{entidad}.py")
|
||||
with open(ruta, "w", encoding="utf-8") as f:
|
||||
f.write(codigo_limpio)
|
||||
|
||||
print(f"✅ Archivo guardado en {ruta}")
|
||||
|
||||
boton_guardar
|
||||
return
|
||||
|
||||
|
||||
@app.cell
|
||||
def _(mo, run_button_db):
|
||||
from backend.db.session import engine
|
||||
from backend.db.base import Base
|
||||
|
||||
# # -> AQUI IMPORTA TUS MODELOS para que se registren en Base.metadata
|
||||
# from domains.Clientes import ClientesModel
|
||||
# from domains.Vehiculos import VehiculosModel
|
||||
|
||||
# from domains.ejemplo import ejemploModel
|
||||
|
||||
from domains.nota import notaModel
|
||||
|
||||
|
||||
def create_all_tables() -> dict:
|
||||
"""Create all tables bound to Base.metadata and return a summary."""
|
||||
# Ensure models are imported so their metadata is registered (done in Cell 1)
|
||||
Base.metadata.create_all(bind=engine)
|
||||
created_tables = list(Base.metadata.tables.keys())
|
||||
return {
|
||||
"message": "✔ Tablas creadas",
|
||||
"tables": created_tables,
|
||||
"count": len(created_tables),
|
||||
}
|
||||
|
||||
_status_message = "Importa tus modelos y pulsa el botón para crear las tablas."
|
||||
result_payload = None # Default so dependents don't break
|
||||
|
||||
if run_button_db.value:
|
||||
try:
|
||||
result_payload = create_all_tables()
|
||||
_status_message = "Operación completada correctamente."
|
||||
except Exception as exc:
|
||||
_status_message = f"Error al crear tablas: {exc!s}"
|
||||
|
||||
mo.vstack([
|
||||
mo.md(f"**Estado:** {_status_message}"),
|
||||
mo.json(result_payload) if result_payload is not None else mo.md("")
|
||||
])
|
||||
return (engine,)
|
||||
|
||||
|
||||
@app.cell
|
||||
def _(mo):
|
||||
run_button_db = mo.ui.run_button(label="Crear tablas en la base de datos", kind="neutral")
|
||||
|
||||
mo.vstack([mo.md("Finalmente registrar las tablas en la bbdd"),
|
||||
run_button_db])
|
||||
return (run_button_db,)
|
||||
|
||||
|
||||
@app.cell
|
||||
def _(mo):
|
||||
# Cell 2
|
||||
run_button_drop = mo.ui.run_button(label="Eliminar todas las tablas vacías", kind = "danger" )
|
||||
run_button_drop
|
||||
return (run_button_drop,)
|
||||
|
||||
|
||||
@app.cell
|
||||
def _(engine, mo, run_button_drop):
|
||||
from sqlalchemy import inspect, func, select
|
||||
from sqlalchemy import MetaData
|
||||
from sqlalchemy import MetaData, select, exists
|
||||
|
||||
def drop_all_empty_tables_improved():
|
||||
engine.dispose()
|
||||
metadata = MetaData()
|
||||
metadata.reflect(bind=engine, resolve_fks=False)
|
||||
|
||||
dropped, skipped = [], []
|
||||
with engine.connect() as connection:
|
||||
trans = connection.begin()
|
||||
try:
|
||||
for table in metadata.sorted_tables:
|
||||
col = list(table.c)[0] # primera columna para usar en exists()
|
||||
has_data = connection.execute(
|
||||
select(exists().where(col != None))
|
||||
).scalar()
|
||||
|
||||
if not has_data:
|
||||
table.drop(engine, checkfirst=True)
|
||||
dropped.append(table.name)
|
||||
else:
|
||||
skipped.append(table.name)
|
||||
trans.commit()
|
||||
except:
|
||||
trans.rollback()
|
||||
raise
|
||||
|
||||
return {"dropped": dropped, "skipped": skipped}
|
||||
|
||||
|
||||
_status_message = "Pulsa el botón para eliminar tablas vacías."
|
||||
_result_payload = None
|
||||
|
||||
if run_button_drop.value:
|
||||
try:
|
||||
_result_payload = drop_all_empty_tables_improved()
|
||||
_status_message = "Operación completada correctamente."
|
||||
except Exception as exc:
|
||||
_status_message = f"Error al eliminar tablas: {exc!s}"
|
||||
|
||||
mo.vstack([
|
||||
mo.md(f"**Estado:** {_status_message}"),
|
||||
mo.json(_result_payload) if _result_payload else mo.md("")
|
||||
])
|
||||
return
|
||||
|
||||
|
||||
@app.cell
|
||||
def _():
|
||||
# # Obtenemos las variables de nuestro template
|
||||
|
||||
# from jinja2 import Environment, meta
|
||||
|
||||
# # Crear un entorno de Jinja
|
||||
# env = Environment()
|
||||
|
||||
# with open(os.path.join(RUTA_CARPETA_SNIPPETS, "markdown/plantilla_mmr.md"), "r", encoding="utf-8") as _f:
|
||||
# source = _f.read()
|
||||
|
||||
# # Parsear la plantilla sin renderizar
|
||||
# parsed_content = env.parse(source)
|
||||
|
||||
# # Obtener las variables no declaradas
|
||||
# variables = meta.find_undeclared_variables(parsed_content)
|
||||
|
||||
# print("Variables encontradas:", variables)
|
||||
return
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
||||
Reference in New Issue
Block a user