import streamlit as st from sqlalchemy import create_engine, Column, Integer, String, Float, ForeignKey, Enum from sqlalchemy.orm import sessionmaker, relationship, declarative_base from datetime import datetime import pytz import os import requests from dotenv import load_dotenv from pydantic import BaseModel from typing import List st.set_page_config( page_title="Gesti贸n de Ingresos y Gastos", layout="centered", initial_sidebar_state="collapsed", page_icon="馃挵", ) # Cargar variables de entorno load_dotenv() # URL de la API de Google Apps Script GOOGLE_SHEET_API_URL = os.getenv("GOOGLE_SHEET_API_URL") # Conexi贸n con la base de datos SQLite (Finanzas.db) DATABASE_URL = "sqlite:///finanzas.db" engine = create_engine(DATABASE_URL) # Crear una clase base para el ORM Base = declarative_base() # Definici贸n de modelos ORM class Usuario(Base): __tablename__ = "usuarios" id_usuario = Column(Integer, primary_key=True, index=True) nombre = Column(String, nullable=False) class Categoria(Base): __tablename__ = "categorias" id_categoria = Column(Integer, primary_key=True, autoincrement=True) nombre_categoria = Column(String, nullable=False) tipo = Column(Enum("Ingreso", "Gasto", name="tipo_categoria"), nullable=False) class Subcategoria(Base): __tablename__ = "subcategorias" id_subcategoria = Column(Integer, primary_key=True, autoincrement=True) id_categoria = Column( Integer, ForeignKey("categorias.id_categoria"), nullable=False ) nombre_subcategoria = Column(String, nullable=False) categoria = relationship("Categoria", backref="subcategorias") class Transaccion(Base): __tablename__ = "transacciones" id_transaccion = Column(Integer, primary_key=True, autoincrement=True) fecha = Column(String, nullable=False) tipo = Column(Enum("Ingreso", "Gasto", name="tipo_transaccion"), nullable=False) monto = Column(Float, nullable=False) id_subcategoria = Column( Integer, ForeignKey("subcategorias.id_subcategoria"), nullable=False ) descripcion = Column(String) id_usuario = Column(Integer, ForeignKey("usuarios.id_usuario"), nullable=False) subcategoria = relationship("Subcategoria", backref="transacciones") usuario = relationship("Usuario", backref="transacciones") # Funci贸n para obtener la fecha y hora precisa en la zona horaria de Per煤 def get_peru_time(): peru_tz = pytz.timezone("America/Lima") return datetime.now(peru_tz).strftime("%Y-%m-%d %H:%M:%S") # Funci贸n para enviar datos a la hoja de Google Sheets def send_to_google_sheet(data: List[BaseModel]): """ Env铆a los datos a la Google Sheet mediante la API de Google Apps Script. :param data: Lista de registros con toda la informaci贸n. """ # Aseg煤rate de convertir los objetos Pydantic en diccionarios data_dicts = [ record.model_dump() for record in data ] # Convertir cada objeto en un diccionario # Enviar los datos como una lista de diccionarios response = requests.post(GOOGLE_SHEET_API_URL, json=data_dicts) if response.status_code == 200: print("Datos enviados con 茅xito a la Google Sheet.") else: print(f"Error al enviar datos: {response.status_code}, {response.text}") # Crear una sesi贸n SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) def insert_transaccion(fecha, tipo, monto, id_subcategoria, descripcion, id_usuario): # Crear una nueva sesi贸n db = SessionLocal() try: # Crear la transacci贸n transaccion = Transaccion( fecha=fecha, tipo=tipo, monto=monto, descripcion=descripcion, id_subcategoria=id_subcategoria, id_usuario=id_usuario, ) db.add(transaccion) db.commit() # Obtener los detalles de la transacci贸n (subcategor铆a, categor铆a, etc.) transaccion_db = ( db.query(Transaccion) .filter(Transaccion.id_transaccion == transaccion.id_transaccion) .first() ) if transaccion_db: subcategoria = transaccion_db.subcategoria.nombre_subcategoria categoria = transaccion_db.subcategoria.categoria.nombre_categoria usuario = transaccion_db.usuario.nombre # Verificar los valores antes de crear el Pydantic # print( # f"Subcategor铆a: {subcategoria}, Categor铆a: {categoria}, Usuario: {usuario}" # ) # Pydantic model para la transacci贸n transaccion_pydantic = TransaccionPydantic( fecha=fecha, tipo=tipo, monto=monto, descripcion=descripcion, subcategoria=subcategoria, categoria=categoria, usuario=usuario, ) # Verificar si el modelo Pydantic tiene datos v谩lidos # print(f"Datos a enviar: {transaccion_pydantic.model_dump()}") send_to_google_sheet([transaccion_pydantic]) # print(transaccion_pydantic.model_dump()) finally: db.close() # Funci贸n para obtener los usuarios def get_usuarios(): db = SessionLocal() try: return db.query(Usuario).all() finally: db.close() # Funci贸n para obtener las categor铆as def get_categorias(): db = SessionLocal() try: return db.query(Categoria).all() finally: db.close() # Funci贸n para obtener las subcategor铆as por categor铆a def get_subcategorias(id_categoria=None): db = SessionLocal() try: if id_categoria: return ( db.query(Subcategoria) .filter(Subcategoria.id_categoria == id_categoria) .all() ) return db.query(Subcategoria).all() finally: db.close() # Funci贸n para obtener las transacciones completas def get_full_transacciones(): db = SessionLocal() try: result = ( db.query(Transaccion).join(Subcategoria).join(Categoria).join(Usuario).all() ) return [ { "id_transaccion": t.id_transaccion, "fecha": t.fecha, "tipo": t.tipo, "monto": t.monto, "descripcion": t.descripcion, "nombre_subcategoria": t.subcategoria.nombre_subcategoria, "nombre_categoria": t.subcategoria.categoria.nombre_categoria, "usuario": t.usuario.nombre, } for t in result ] finally: db.close() # Definici贸n de modelos Pydantic class TransaccionPydantic(BaseModel): fecha: str tipo: str monto: float descripcion: str subcategoria: str categoria: str usuario: str class CategoriaPydantic(BaseModel): id_categoria: int nombre_categoria: str tipo: str class SubcategoriaPydantic(BaseModel): id_subcategoria: int nombre_subcategoria: str class UsuarioPydantic(BaseModel): id_usuario: int nombre: str # Interfaz de Streamlit st.title("Gesti贸n de Ingresos y Gastos") menu = st.sidebar.selectbox("Men煤", ["Registrar Transacci贸n", "Ver Transacciones"]) if menu == "Registrar Transacci贸n": st.header("Registrar Transacci贸n") # Selecci贸n de usuario usuarios = get_usuarios() usuario = st.selectbox( "Usuario", [(u.id_usuario, u.nombre) for u in usuarios], format_func=lambda x: x[1], ) id_usuario = usuario[0] # Selecci贸n de tipo (Ingreso o Gasto) tipo = st.selectbox("Tipo", ["Ingreso", "Gasto"]) # Selecci贸n de categor铆a categorias = [c for c in get_categorias() if c.tipo == tipo] categoria = st.selectbox( "Categor铆a", categorias, format_func=lambda x: x.nombre_categoria ) id_categoria = categoria.id_categoria # Selecci贸n de subcategor铆a subcategorias = get_subcategorias(id_categoria) subcategoria = st.selectbox( "Subcategor铆a", subcategorias, format_func=lambda x: x.nombre_subcategoria ) id_subcategoria = subcategoria.id_subcategoria # Datos de la transacci贸n monto = st.number_input("Monto", min_value=0.0, step=0.01, format="%.2f") descripcion = st.text_area("Descripci贸n") fecha = get_peru_time() # Obtener la hora y fecha precisa en zona horaria de Per煤 if st.button("Guardar Transacci贸n"): insert_transaccion( fecha, tipo, monto, id_subcategoria, descripcion, id_usuario, ) st.success("隆Transacci贸n registrada con 茅xito!") elif menu == "Ver Transacciones": st.header("Ver Transacciones") transacciones = get_full_transacciones() # Mostrar en una tabla st.dataframe( transacciones, use_container_width=True, hide_index=True, )