Spaces:
Sleeping
Sleeping
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 | |
from flask import Flask, jsonify, render_template | |
from flask import request, redirect, url_for, flash | |
from flask_cors import CORS | |
app = Flask(__name__) | |
app.secret_key = "Jairo_y_Diana" | |
CORS(app) | |
def add_cors_headers(response): | |
response.headers["Access-Control-Allow-Origin"] = "*" | |
response.headers["Access-Control-Allow-Headers"] = "*" | |
response.headers["Access-Control-Allow-Methods"] = "*" | |
response.headers["Access-Control-Allow-Credentials"] = "true" | |
return response | |
# 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): | |
data_dicts = [] | |
for data in data_list: | |
# Convertir la fecha a un string en el formato adecuado | |
data_dict = data.model_dump() | |
if isinstance(data_dict["fecha"], datetime): | |
data_dict["fecha"] = data_dict["fecha"].strftime("%Y-%m-%d %H:%M:%S") | |
data_dicts.append(data_dict) | |
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}") | |
# 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. | |
# """ | |
# data_dicts = [record.model_dump() for record in data] | |
# 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}") | |
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) | |
def insert_transaccion(fecha, tipo, monto, id_subcategoria, descripcion, id_usuario): | |
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() | |
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 | |
transaccion_pydantic = TransaccionPydantic( | |
fecha=fecha, # Usar fecha directamente | |
tipo=tipo, | |
monto=monto, | |
descripcion=descripcion, | |
subcategoria=subcategoria, | |
categoria=categoria, | |
usuario=usuario, | |
) | |
print(transaccion_pydantic.model_dump()) | |
send_to_google_sheet([transaccion_pydantic]) | |
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 categor铆as por tipo | |
def get_categorias_por_tipo(tipo): | |
db = SessionLocal() | |
try: | |
return db.query(Categoria).filter(Categoria.tipo == tipo).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() | |
def get_tipos(): | |
return ["Ingreso", "Gasto"] | |
# Definici贸n de modelos Pydantic | |
class TransaccionPydantic(BaseModel): | |
fecha: datetime | |
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 | |
class TransaccionIngreso(BaseModel): | |
fecha: datetime | |
monto: float | |
tipo: str | |
id_subcategoria: int | |
descripcion: str | |
id_usuario: int | |
# rutas | |
def usuarios(): | |
usuarios = get_usuarios() | |
return {"usuarios": usuarios} | |
def categorias(): | |
categorias = get_categorias() | |
return {"categorias": categorias} | |
# Ruta para obtener categor铆as por tipo a trav茅s de AJAX | |
def categorias_por_tipo(tipo): | |
categorias = get_categorias_por_tipo(tipo) # Obtener las categor铆as por el tipo | |
return jsonify( | |
[ | |
{ | |
"id_categoria": categoria.id_categoria, | |
"nombre_categoria": categoria.nombre_categoria, | |
"tipo": categoria.tipo, | |
} | |
for categoria in categorias | |
] | |
) | |
def subcategorias(): | |
subcategorias = get_subcategorias() | |
return {"subcategorias": subcategorias} | |
# Ruta para obtener subcategor铆as por categor铆a a trav茅s de AJAX | |
def subcategorias_por_categoria(id_categoria): | |
subcategorias = get_subcategorias(id_categoria) | |
return jsonify( | |
[ | |
{ | |
"id_subcategoria": subcategoria.id_subcategoria, | |
"nombre_subcategoria": subcategoria.nombre_subcategoria, | |
} | |
for subcategoria in subcategorias | |
] | |
) | |
# Rutas del API | |
def home(): | |
usuarios = get_usuarios() | |
categorias = get_categorias() | |
subcategorias = get_subcategorias() | |
tipos = get_tipos() | |
return render_template( | |
"index.html", | |
usuarios=usuarios, | |
categorias=categorias, | |
subcategorias=subcategorias, | |
tipos=tipos, | |
) | |
def guardar_transaccion(): | |
try: | |
# Obtener datos del formulario | |
id_usuario = request.form.get("usuario") | |
fecha = datetime.now() # Asigna la fecha actual | |
tipo = request.form.get("tipo") | |
monto = float(request.form.get("monto", 0)) # Convertir a float | |
id_subcategoria = request.form.get("subcategoria") | |
descripcion = request.form.get("descripcion") | |
# Validar que todos los campos est茅n presentes | |
if not all([id_usuario, tipo, monto, id_subcategoria]): | |
flash("Por favor complete todos los campos", "error") | |
return redirect(url_for("home")) # Redirigir al formulario principal | |
# Insertar la transacci贸n | |
insert_transaccion( | |
id_usuario=int(id_usuario), | |
fecha=fecha, | |
tipo=tipo, | |
monto=monto, | |
id_subcategoria=int(id_subcategoria), | |
descripcion=descripcion, | |
) | |
flash("Transacci贸n guardada exitosamente", "success") | |
return redirect(url_for("home")) # Redirigir al formulario principal | |
except Exception as e: | |
flash(f"Error al guardar la transacci贸n: {str(e)}", "error") | |
return redirect(url_for("home")) # Redirigir al formulario principal | |
if __name__ == "__main__": | |
app.run(host="0.0.0.0", port=7860, debug=False) | |