JairoDanielMT commited on
Commit
4eabb54
·
verified ·
1 Parent(s): 96cf5b2

Upload 4 files

Browse files
Files changed (4) hide show
  1. .streamlit/config.toml +6 -0
  2. finanzas.db +0 -0
  3. main.py +308 -0
  4. requirements.txt +5 -0
.streamlit/config.toml ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ [theme]
2
+ primaryColor="#53ba83"
3
+ backgroundColor="#000020"
4
+ secondaryBackgroundColor="#2f2c79"
5
+ textColor="#ffffff"
6
+ font="serif"
finanzas.db ADDED
Binary file (24.6 kB). View file
 
main.py ADDED
@@ -0,0 +1,308 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from sqlalchemy import create_engine, Column, Integer, String, Float, ForeignKey, Enum
3
+ from sqlalchemy.orm import sessionmaker, relationship, declarative_base
4
+ from datetime import datetime
5
+ import pytz
6
+ import os
7
+ import requests
8
+ from dotenv import load_dotenv
9
+ from pydantic import BaseModel
10
+ from typing import List
11
+
12
+ st.set_page_config(
13
+ page_title="Gestión de Ingresos y Gastos",
14
+ layout="centered",
15
+ initial_sidebar_state="collapsed",
16
+ page_icon="💰",
17
+ )
18
+
19
+ # Cargar variables de entorno
20
+ load_dotenv()
21
+
22
+ # URL de la API de Google Apps Script
23
+ GOOGLE_SHEET_API_URL = os.getenv("GOOGLE_SHEET_API_URL")
24
+
25
+ # Conexión con la base de datos SQLite (Finanzas.db)
26
+ DATABASE_URL = "sqlite:///finanzas.db"
27
+ engine = create_engine(DATABASE_URL)
28
+
29
+ # Crear una clase base para el ORM
30
+ Base = declarative_base()
31
+
32
+ # Definición de modelos ORM
33
+
34
+
35
+ class Usuario(Base):
36
+ __tablename__ = "usuarios"
37
+
38
+ id_usuario = Column(Integer, primary_key=True, index=True)
39
+ nombre = Column(String, nullable=False)
40
+
41
+
42
+ class Categoria(Base):
43
+ __tablename__ = "categorias"
44
+
45
+ id_categoria = Column(Integer, primary_key=True, autoincrement=True)
46
+ nombre_categoria = Column(String, nullable=False)
47
+ tipo = Column(Enum("Ingreso", "Gasto", name="tipo_categoria"), nullable=False)
48
+
49
+
50
+ class Subcategoria(Base):
51
+ __tablename__ = "subcategorias"
52
+
53
+ id_subcategoria = Column(Integer, primary_key=True, autoincrement=True)
54
+ id_categoria = Column(
55
+ Integer, ForeignKey("categorias.id_categoria"), nullable=False
56
+ )
57
+ nombre_subcategoria = Column(String, nullable=False)
58
+
59
+ categoria = relationship("Categoria", backref="subcategorias")
60
+
61
+
62
+ class Transaccion(Base):
63
+ __tablename__ = "transacciones"
64
+
65
+ id_transaccion = Column(Integer, primary_key=True, autoincrement=True)
66
+ fecha = Column(String, nullable=False)
67
+ tipo = Column(Enum("Ingreso", "Gasto", name="tipo_transaccion"), nullable=False)
68
+ monto = Column(Float, nullable=False)
69
+ id_subcategoria = Column(
70
+ Integer, ForeignKey("subcategorias.id_subcategoria"), nullable=False
71
+ )
72
+ descripcion = Column(String)
73
+ id_usuario = Column(Integer, ForeignKey("usuarios.id_usuario"), nullable=False)
74
+
75
+ subcategoria = relationship("Subcategoria", backref="transacciones")
76
+ usuario = relationship("Usuario", backref="transacciones")
77
+
78
+
79
+ # Función para obtener la fecha y hora precisa en la zona horaria de Perú
80
+ def get_peru_time():
81
+ peru_tz = pytz.timezone("America/Lima")
82
+ return datetime.now(peru_tz).strftime("%Y-%m-%d %H:%M:%S")
83
+
84
+
85
+ # Función para enviar datos a la hoja de Google Sheets
86
+ def send_to_google_sheet(data: List[BaseModel]):
87
+ """
88
+ Envía los datos a la Google Sheet mediante la API de Google Apps Script.
89
+ :param data: Lista de registros con toda la información.
90
+ """
91
+ # Asegúrate de convertir los objetos Pydantic en diccionarios
92
+ data_dicts = [
93
+ record.model_dump() for record in data
94
+ ] # Convertir cada objeto en un diccionario
95
+
96
+ # Enviar los datos como una lista de diccionarios
97
+ response = requests.post(GOOGLE_SHEET_API_URL, json=data_dicts)
98
+
99
+ if response.status_code == 200:
100
+ print("Datos enviados con éxito a la Google Sheet.")
101
+ else:
102
+ print(f"Error al enviar datos: {response.status_code}, {response.text}")
103
+
104
+
105
+ # Crear una sesión
106
+ SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
107
+
108
+
109
+ def insert_transaccion(fecha, tipo, monto, id_subcategoria, descripcion, id_usuario):
110
+ # Crear una nueva sesión
111
+ db = SessionLocal()
112
+
113
+ try:
114
+ # Crear la transacción
115
+ transaccion = Transaccion(
116
+ fecha=fecha,
117
+ tipo=tipo,
118
+ monto=monto,
119
+ descripcion=descripcion,
120
+ id_subcategoria=id_subcategoria,
121
+ id_usuario=id_usuario,
122
+ )
123
+ db.add(transaccion)
124
+ db.commit()
125
+
126
+ # Obtener los detalles de la transacción (subcategoría, categoría, etc.)
127
+ transaccion_db = (
128
+ db.query(Transaccion)
129
+ .filter(Transaccion.id_transaccion == transaccion.id_transaccion)
130
+ .first()
131
+ )
132
+
133
+ if transaccion_db:
134
+ subcategoria = transaccion_db.subcategoria.nombre_subcategoria
135
+ categoria = transaccion_db.subcategoria.categoria.nombre_categoria
136
+ usuario = transaccion_db.usuario.nombre
137
+
138
+ # Verificar los valores antes de crear el Pydantic
139
+ # print(
140
+ # f"Subcategoría: {subcategoria}, Categoría: {categoria}, Usuario: {usuario}"
141
+ # )
142
+
143
+ # Pydantic model para la transacción
144
+ transaccion_pydantic = TransaccionPydantic(
145
+ fecha=fecha,
146
+ tipo=tipo,
147
+ monto=monto,
148
+ descripcion=descripcion,
149
+ subcategoria=subcategoria,
150
+ categoria=categoria,
151
+ usuario=usuario,
152
+ )
153
+ # Verificar si el modelo Pydantic tiene datos válidos
154
+ # print(f"Datos a enviar: {transaccion_pydantic.model_dump()}")
155
+
156
+ send_to_google_sheet([transaccion_pydantic])
157
+ # print(transaccion_pydantic.model_dump())
158
+
159
+ finally:
160
+ db.close()
161
+
162
+
163
+ # Función para obtener los usuarios
164
+ def get_usuarios():
165
+ db = SessionLocal()
166
+ try:
167
+ return db.query(Usuario).all()
168
+ finally:
169
+ db.close()
170
+
171
+
172
+ # Función para obtener las categorías
173
+ def get_categorias():
174
+ db = SessionLocal()
175
+ try:
176
+ return db.query(Categoria).all()
177
+ finally:
178
+ db.close()
179
+
180
+
181
+ # Función para obtener las subcategorías por categoría
182
+ def get_subcategorias(id_categoria=None):
183
+ db = SessionLocal()
184
+ try:
185
+ if id_categoria:
186
+ return (
187
+ db.query(Subcategoria)
188
+ .filter(Subcategoria.id_categoria == id_categoria)
189
+ .all()
190
+ )
191
+ return db.query(Subcategoria).all()
192
+ finally:
193
+ db.close()
194
+
195
+
196
+ # Función para obtener las transacciones completas
197
+ def get_full_transacciones():
198
+ db = SessionLocal()
199
+ try:
200
+ result = (
201
+ db.query(Transaccion).join(Subcategoria).join(Categoria).join(Usuario).all()
202
+ )
203
+ return [
204
+ {
205
+ "id_transaccion": t.id_transaccion,
206
+ "fecha": t.fecha,
207
+ "tipo": t.tipo,
208
+ "monto": t.monto,
209
+ "descripcion": t.descripcion,
210
+ "nombre_subcategoria": t.subcategoria.nombre_subcategoria,
211
+ "nombre_categoria": t.subcategoria.categoria.nombre_categoria,
212
+ "usuario": t.usuario.nombre,
213
+ }
214
+ for t in result
215
+ ]
216
+ finally:
217
+ db.close()
218
+
219
+
220
+ # Definición de modelos Pydantic
221
+
222
+
223
+ class TransaccionPydantic(BaseModel):
224
+ fecha: str
225
+ tipo: str
226
+ monto: float
227
+ descripcion: str
228
+ subcategoria: str
229
+ categoria: str
230
+ usuario: str
231
+
232
+
233
+ class CategoriaPydantic(BaseModel):
234
+ id_categoria: int
235
+ nombre_categoria: str
236
+ tipo: str
237
+
238
+
239
+ class SubcategoriaPydantic(BaseModel):
240
+ id_subcategoria: int
241
+ nombre_subcategoria: str
242
+
243
+
244
+ class UsuarioPydantic(BaseModel):
245
+ id_usuario: int
246
+ nombre: str
247
+
248
+
249
+ # Interfaz de Streamlit
250
+ st.title("Gestión de Ingresos y Gastos")
251
+
252
+ menu = st.sidebar.selectbox("Menú", ["Registrar Transacción", "Ver Transacciones"])
253
+
254
+ if menu == "Registrar Transacción":
255
+ st.header("Registrar Transacción")
256
+
257
+ # Selección de usuario
258
+ usuarios = get_usuarios()
259
+ usuario = st.selectbox(
260
+ "Usuario",
261
+ [(u.id_usuario, u.nombre) for u in usuarios],
262
+ format_func=lambda x: x[1],
263
+ )
264
+ id_usuario = usuario[0]
265
+
266
+ # Selección de tipo (Ingreso o Gasto)
267
+ tipo = st.selectbox("Tipo", ["Ingreso", "Gasto"])
268
+
269
+ # Selección de categoría
270
+ categorias = [c for c in get_categorias() if c.tipo == tipo]
271
+ categoria = st.selectbox(
272
+ "Categoría", categorias, format_func=lambda x: x.nombre_categoria
273
+ )
274
+ id_categoria = categoria.id_categoria
275
+
276
+ # Selección de subcategoría
277
+ subcategorias = get_subcategorias(id_categoria)
278
+ subcategoria = st.selectbox(
279
+ "Subcategoría", subcategorias, format_func=lambda x: x.nombre_subcategoria
280
+ )
281
+ id_subcategoria = subcategoria.id_subcategoria
282
+
283
+ # Datos de la transacción
284
+ monto = st.number_input("Monto", min_value=0.0, step=0.01, format="%.2f")
285
+ descripcion = st.text_area("Descripción")
286
+ fecha = get_peru_time() # Obtener la hora y fecha precisa en zona horaria de Perú
287
+
288
+ if st.button("Guardar Transacción"):
289
+ insert_transaccion(
290
+ fecha,
291
+ tipo,
292
+ monto,
293
+ id_subcategoria,
294
+ descripcion,
295
+ id_usuario,
296
+ )
297
+ st.success("¡Transacción registrada con éxito!")
298
+
299
+ elif menu == "Ver Transacciones":
300
+ st.header("Ver Transacciones")
301
+ transacciones = get_full_transacciones()
302
+
303
+ # Mostrar en una tabla
304
+ st.dataframe(
305
+ transacciones,
306
+ use_container_width=True,
307
+ hide_index=True,
308
+ )
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ pandas
2
+ python-dotenv
3
+ sqlalchemy
4
+ pymysql
5
+ pydantic