Proyecto Final: Aplicacion Completa con Python
Ha llegado el momento de integrar todo lo aprendido en el curso. Construiremos un sistema completo de gestion de inventario con las siguientes caracteristicas: lectura y escritura de archivos CSV/JSON, OOP, manejo de errores, consumo de API externa y generacion de reportes.
Descripcion del proyectoSistema de Gestion de Inventario con Precios Actualizados:
- Carga productos desde un CSV
- Obtiene tipo de cambio de una API real
- Calcula precios en multiples monedas
- Detecta productos con bajo stock y proximos a vencer
- Genera reportes en TXT y JSON
- Interfaz de linea de comandos interactiva
inventario_app/
main.py # Punto de entrada
modelos.py # Clases Producto, Inventario
api_cliente.py # Consumo de APIs externas
reportes.py # Generacion de reportes
datos/
productos.csv # Datos iniciales
config.json # Configuracion
modelos.py
from datetime import date
from abc import ABC, abstractmethod
class Producto(ABC):
_contador = 0
def __init__(self, nombre, precio_usd, cantidad, categoria):
Producto._contador += 1
self.id = Producto._contador
self.nombre = nombre
self.precio_usd = precio_usd
self.cantidad = cantidad
self.categoria = categoria
def precio_en(self, moneda, tasa_cambio):
return self.precio_usd * tasa_cambio
@property
def valor_total_usd(self):
return self.precio_usd * self.cantidad
@abstractmethod
def es_vendible(self):
pass
@abstractmethod
def alerta(self):
pass
def __str__(self):
return f'[{self.id}] {self.nombre} (${self.precio_usd}) x{self.cantidad}'
def __lt__(self, other):
return self.precio_usd < other.precio_usd
class ProductoPerecible(Producto):
def __init__(self, nombre, precio_usd, cantidad, categoria, fecha_vencimiento):
super().__init__(nombre, precio_usd, cantidad, categoria)
self.fecha_vencimiento = fecha_vencimiento
@property
def dias_restantes(self):
return (self.fecha_vencimiento - date.today()).days
def es_vendible(self):
return self.cantidad > 0 and self.dias_restantes > 0
def alerta(self):
if self.dias_restantes <= 0:
return f'VENCIDO: {self.nombre}'
if self.dias_restantes <= 7:
return f'URGENTE: {self.nombre} vence en {self.dias_restantes} dias'
if self.cantidad <= 5:
return f'BAJO STOCK: {self.nombre} ({self.cantidad} unidades)'
return None
class ProductoGeneral(Producto):
def es_vendible(self):
return self.cantidad > 0
def alerta(self):
if self.cantidad <= 5:
return f'BAJO STOCK: {self.nombre} ({self.cantidad} unidades)'
return None
class Inventario:
def __init__(self):
self._productos = {}
def agregar(self, producto):
self._productos[producto.id] = producto
def buscar(self, id_o_nombre):
if isinstance(id_o_nombre, int):
return self._productos.get(id_o_nombre)
return [
p for p in self._productos.values()
if id_o_nombre.lower() in p.nombre.lower()
]
def vender(self, id_producto, cantidad):
p = self._productos.get(id_producto)
if not p:
raise ValueError('Producto no encontrado')
if not p.es_vendible():
raise ValueError(f'{p.nombre} no disponible')
if cantidad > p.cantidad:
raise ValueError(f'Stock insuficiente: solo hay {p.cantidad}')
p.cantidad -= cantidad
return p.precio_usd * cantidad
def alertas(self):
return [a for p in self._productos.values() if (a := p.alerta())]
def valor_total_usd(self):
return sum(p.valor_total_usd for p in self._productos.values())
def __len__(self):
return len(self._productos)
def __iter__(self):
return iter(self._productos.values())
def todos(self):
return list(self._productos.values())
api_cliente.py
import requests
import json
from pathlib import Path
from datetime import datetime, timedelta
def obtener_tipo_cambio(moneda='ARS', cache_horas=6):
cache_file = Path(f'.cache_tasa_{moneda}.json')
if cache_file.exists():
cached = json.loads(cache_file.read_text())
edad = datetime.now() - datetime.fromisoformat(cached['timestamp'])
if edad < timedelta(hours=cache_horas):
print(f'[Cache] Tasa USD/{moneda}: {cached["tasa"]}')
return cached['tasa']
try:
respuesta = requests.get(
f'https://open.er-api.com/v6/latest/USD',
timeout=10
)
respuesta.raise_for_status()
datos = respuesta.json()
tasa = datos['rates'].get(moneda, 1.0)
cache_file.write_text(json.dumps({
'timestamp': datetime.now().isoformat(),
'tasa': tasa
}))
print(f'[API] Tasa USD/{moneda}: {tasa}')
return tasa
except Exception as e:
print(f'Error obteniendo tasa: {e}. Usando tasa 1.0')
return 1.0
reportes.py
import csv
import json
from datetime import datetime
from pathlib import Path
def guardar_reporte_txt(inventario, tasa_ars, archivo='reporte.txt'):
with open(archivo, 'w', encoding='utf-8') as f:
f.write('REPORTE DE INVENTARIO\n')
f.write('=' * 50 + '\n')
f.write(f'Generado: {datetime.now().strftime("%Y-%m-%d %H:%M")}\n')
f.write(f'Total productos: {len(inventario)}\n')
f.write(f'Valor total USD: {inventario.valor_total_usd():.2f}\n')
f.write(f'Valor total ARS: {inventario.valor_total_usd() * tasa_ars:.2f}\n')
f.write('\n')
alertas = inventario.alertas()
if alertas:
f.write('ALERTAS:\n')
for a in alertas:
f.write(f' - {a}\n')
f.write('\n')
f.write('TODOS LOS PRODUCTOS:\n')
f.write('-' * 50 + '\n')
for p in sorted(inventario):
precio_ars = p.precio_usd * tasa_ars
f.write(f'{p.nombre}\n')
f.write(f' Precio: USD {p.precio_usd} / ARS {precio_ars:.0f}\n')
f.write(f' Stock: {p.cantidad} | Categoria: {p.categoria}\n')
print(f'Reporte guardado en {archivo}')
def exportar_csv(inventario, archivo='inventario_export.csv'):
with open(archivo, 'w', newline='', encoding='utf-8') as f:
campos = ['id', 'nombre', 'precio_usd', 'cantidad', 'categoria', 'valor_total']
writer = csv.DictWriter(f, fieldnames=campos)
writer.writeheader()
for p in inventario:
writer.writerow({
'id': p.id,
'nombre': p.nombre,
'precio_usd': p.precio_usd,
'cantidad': p.cantidad,
'categoria': p.categoria,
'valor_total': p.valor_total_usd
})
print(f'CSV exportado: {archivo}')
main.py
import csv
from datetime import date, timedelta
from modelos import Inventario, ProductoGeneral, ProductoPerecible
from api_cliente import obtener_tipo_cambio
from reportes import guardar_reporte_txt, exportar_csv
def cargar_datos_ejemplo(inventario):
productos = [
ProductoGeneral('Laptop Gaming', 1200, 5, 'Computadoras'),
ProductoGeneral('Mouse Inalambrico', 35, 50, 'Accesorios'),
ProductoGeneral('Monitor 4K', 450, 3, 'Monitores'),
ProductoGeneral('Teclado Mecanico', 80, 4, 'Accesorios'),
ProductoPerecible('Bateria AA x4', 5.99, 100, 'Accesorios', date.today() + timedelta(365)),
ProductoPerecible('Pantalla Tactil', 299, 2, 'Componentes', date.today() + timedelta(5)),
]
for p in productos:
inventario.agregar(p)
def menu_principal():
inventario = Inventario()
cargar_datos_ejemplo(inventario)
tasa_ars = obtener_tipo_cambio('ARS')
while True:
print('\n=== SISTEMA DE INVENTARIO ===')
print('1. Ver todos los productos')
print('2. Buscar producto')
print('3. Registrar venta')
print('4. Ver alertas')
print('5. Generar reporte TXT')
print('6. Exportar CSV')
print('7. Ver valor total del inventario')
print('0. Salir')
opcion = input('\nOpcion: ').strip()
if opcion == '1':
print('\nProductos:')
for p in sorted(inventario):
precio_ars = p.precio_usd * tasa_ars
estado = 'OK' if p.es_vendible() else 'NO VENDIBLE'
print(f' {p} | ARS {precio_ars:.0f} [{estado}]')
elif opcion == '2':
termino = input('Buscar: ')
resultados = inventario.buscar(termino)
for p in resultados:
print(f' {p}')
elif opcion == '3':
try:
id_p = int(input('ID del producto: '))
cant = int(input('Cantidad: '))
total = inventario.vender(id_p, cant)
print(f'Venta exitosa: USD {total:.2f} / ARS {total * tasa_ars:.0f}')
except (ValueError, KeyError) as e:
print(f'Error: {e}')
elif opcion == '4':
alertas = inventario.alertas()
if alertas:
print('\nALERTAS:')
for a in alertas:
print(f' - {a}')
else:
print('Sin alertas activas')
elif opcion == '5':
guardar_reporte_txt(inventario, tasa_ars)
elif opcion == '6':
exportar_csv(inventario)
elif opcion == '7':
total_usd = inventario.valor_total_usd()
print(f'Valor total: USD {total_usd:.2f} / ARS {total_usd * tasa_ars:.0f}')
elif opcion == '0':
print('Hasta pronto!')
break
else:
print('Opcion invalida')
if __name__ == '__main__':
menu_principal()
Como ejecutar el proyecto
# Instalar dependencias
pip install requests
# Crear los archivos en la misma carpeta
# modelos.py, api_cliente.py, reportes.py, main.py
# Ejecutar
python main.py
Que aprendiste en este curso
A lo largo de los 5 modulos del curso cubriste:
Modulo 1 - Fundamentos:
- Variables y tipos de datos
- Operadores y expresiones
- Input/output basico
Modulo 2 - Control de Flujo y Funciones:
- if/elif/else
- Bucles for y while
- Funciones con parametros y return
- Manejo de errores con try/except
Modulo 3 - Estructuras de Datos:
- Listas y tuplas
- Diccionarios y sets
- Comprension de listas
Modulo 4 - Programacion Orientada a Objetos:
- Clases y objetos
- Herencia y polimorfismo
- Metodos especiales
Modulo 5 - Python para el Mundo Real:
- Manejo de archivos TXT, CSV, JSON
- Automatizacion de tareas
- Consumo de APIs REST
- Proyecto final integrador
Revisemos los puntos más importantes de esta lección antes de continuar.
Proximos pasos
Ahora que dominas los fundamentos, aqui tienes caminos para continuar:
- Web con Django o FastAPI: Construye aplicaciones web con Python
- Data Science: pandas, numpy, matplotlib para analisis de datos
- Machine Learning: scikit-learn, TensorFlow, PyTorch
- Automatizacion avanzada: Selenium, Playwright para browser automation
- DevOps: Docker, CI/CD con Python
Sigue practicando. La mejor forma de aprender a programar es construir proyectos reales que te importen. Felicitaciones por completar el curso de Python desde Cero!
- Comprendo el concepto principal y puedo explicarlo con mis palabras
- Entiendo cómo aplicarlo en mi situación especÃfica
- Necesito repasar algunas partes antes de continuar
- Quiero ver más ejemplos prácticos del tema