Objetivo de Aprendizaje
Consolidar los conocimientos adquiridos en el manejo de archivos diseñando, implementando y depurando un sistema completo de préstamos para una biblioteca barrial, integrando el uso de JSON para datos complejos, CSV para registros tabulares y pathlib para la organización de directorios, todo respaldado por un manejo robusto de errores; para resolver un problema real de tu comunidad, desarrollando así un pensamiento computacional estructurado, autónomo y orientado al bienestar colectivo, comprendiendo cómo esta lógica fundamenta el funcionamiento de las bases de datos modernas.
Activación: De Estudiante a Constructor
Imagina que has pasado las últimas semanas aprendiendo a usar un martillo, una sierra, un nivel y una plomada por separado. Sabes cómo clavar un clavo y cómo cortar madera. Pero hoy, alguien te entrega los planos y te dice: "Ahora, construye una casa completa".
No se trata solo de usar las herramientas, sino de saber cuándo y cómo combinarlas para crear algo que funcione, sea útil y dure en el tiempo. Eso es exactamente lo que harás hoy.
La "Biblioteca Barrial San José" de tu comunidad tiene un problema: los libros se pierden, no saben quién tiene qué préstamo, y las multas se calculan a mano en cuadernos desgastados. Tú tienes el poder de cambiar eso. No es un ejercicio más de código; es tu obra maestra. Es el momento de demostrar que puedes transformar líneas de texto en un sistema que resuelve problemas reales.
Desarrollo: Construyendo el Sistema
1. La Estructura del Proyecto
Antes de escribir código, un buen arquitecto organiza su terreno. Nuestro proyecto biblioteca_barrial necesita una estructura clara para separar responsabilidades:
biblioteca_barrial/
├── biblioteca.py # Programa principal
├── config.json # Configuración del sistema (parámetros externos)
├── datos/
│ ├── libros.json # Catálogo de libros (datos complejos y anidados)
│ ├── prestamos.csv # Historial de préstamos (datos tabulares)
│ └── vecinos.json # Registro de vecinos
├── respaldos/ # Respaldos automáticos organizados por fecha
│ └── 2026-07-16/
│ ├── libros_backup.json
│ └── prestamos_backup.csv
└── reportes/ # Reportes generados (ej. vencidos)
└── vencidos_2026-07-16.txt
2. El Código: Tu Obra Maestra
Para facilitar el aprendizaje y la revisión (principio DUA de representación múltiple), el código está organizado en secciones desplegables. Observa cómo cada parte tiene una única responsabilidad.
📦 Importaciones y Configuración
# biblioteca.py - Sistema de Préstamos de Biblioteca Barrial
# Proyecto integrador - Curso de Manejo de Archivos en Python
import json
import csv
from pathlib import Path
from datetime import datetime, timedelta
import shutil
# ============================================================
# CONFIGURACIÓN Y CONSTANTES
# ============================================================
CARPETA_DATOS = Path("datos")
CARPETA_RESPALDOS = Path("respaldos")
CARPETA_REPORTES = Path("reportes")
ARCHIVO_LIBROS = CARPETA_DATOS / "libros.json"
ARCHIVO_PRESTAMOS = CARPETA_DATOS / "prestamos.csv"
ARCHIVO_VECINOS = CARPETA_DATOS / "vecinos.json"
ARCHIVO_CONFIG = Path("config.json")
CONFIG_DEFAULT = {
"biblioteca": {
"nombre": "Biblioteca Barrial San José",
"direccion": "Calle Principal s/n, Barrio San José",
"telefono": "02-234-5678",
"horario": "Lunes a Sábado, 9:00 - 18:00"
},
"prestamos": {
"dias_prestamo": 14,
"max_libros_por_vecino": 3,
"multa_por_dia_vencido": 0.50
},
"sistema": {
"version": "1.0.0",
"ultimo_respaldo": None
}
}
⚙️ Funciones de Inicialización y Carga
# ============================================================
# FUNCIONES DE INICIALIZACIÓN
# ============================================================
def inicializar_sistema():
"""Crea la estructura de carpetas y archivos necesarios"""
print(" 🏛️ Inicializando Sistema de Biblioteca Barrial...")
for carpeta in [CARPETA_DATOS, CARPETA_RESPALDOS, CARPETA_REPORTES]:
carpeta.mkdir(exist_ok=True)
config = cargar_configuracion()
libros = cargar_libros()
prestamos = cargar_prestamos()
vecinos = cargar_vecinos()
print(f" ✅ Sistema inicializado correctamente")
return config, libros, prestamos, vecinos
def cargar_configuracion():
if not ARCHIVO_CONFIG.exists():
print(" ⚠️ Configuración no encontrada. Creando configuración inicial...")
try:
with open(ARCHIVO_CONFIG, "w", encoding="utf-8") as f:
json.dump(CONFIG_DEFAULT, f, indent=4, ensure_ascii=False)
return CONFIG_DEFAULT.copy()
except Exception as e:
print(f" ❌ Error al crear configuración: {e}")
return CONFIG_DEFAULT.copy()
try:
with open(ARCHIVO_CONFIG, "r", encoding="utf-8") as f:
return json.load(f)
except Exception as e:
print(f" ⚠️ Error al cargar configuración: {e}. Usando valores por defecto.")
return CONFIG_DEFAULT.copy()
def cargar_prestamos():
if not ARCHIVO_PRESTAMOS.exists():
guardar_prestamos([])
return []
try:
prestamos = []
with open(ARCHIVO_PRESTAMOS, "r", encoding="utf-8", newline='') as f:
lector = csv.DictReader(f)
for fila in lector:
fila['fecha_prestamo'] = datetime.strptime(fila['fecha_prestamo'], "%d/%m/%Y")
fila['fecha_devolucion'] = datetime.strptime(fila['fecha_devolucion'], "%d/%m/%Y")
prestamos.append(fila)
return prestamos
except Exception as e:
print(f" ❌ Error al cargar préstamos: {e}")
return []
📚 Funciones de Gestión (Libros, Vecinos, Préstamos)
# ============================================================
# FUNCIONES DE GESTIÓN DE LIBROS
# ============================================================
def registrar_libro(libros):
print("\n 📚 Registrar nuevo libro")
titulo = input("Título: ").strip()
autor = input("Autor: ").strip()
id_libro = len(libros) + 1
libro = {
"id": id_libro,
"titulo": titulo,
"autor": autor,
"categoria": "General",
"ubicacion": "Estante A",
"estado": "disponible",
"fecha_registro": datetime.now().strftime("%d/%m/%Y")
}
libros.append(libro)
guardar_libros(libros)
print(f" ✅ Libro registrado exitosamente (ID: {id_libro})")
# ============================================================
# FUNCIONES DE PRÉSTAMOS
# ============================================================
def registrar_prestamo(libros, prestamos, vecinos, config):
print("\n 📖 Registrar préstamo")
disponibles = [l for l in libros if l['estado'] == "disponible"]
if not disponibles:
print(" ❌ No hay libros disponibles para préstamo")
return
# (Lógica simplificada para el ejemplo: selección de libro y vecino)
# Verificación de límite de préstamos:
max_libros = config['prestamos']['max_libros_por_vecino']
# ... lógica de validación y registro ...
print(" ✅ Préstamo registrado exitosamente")
📊 Reportes, Respaldo y Programa Principal
# ============================================================
# FUNCIONES DE RESPALDO
# ============================================================
def crear_respaldo(config):
print("\n 💾 Creando respaldo del sistema...")
fecha_respaldo = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
carpeta_respaldo = CARPETA_RESPALDOS / fecha_respaldo
try:
carpeta_respaldo.mkdir(parents=True, exist_ok=True)
archivos_a_respaldar = [ARCHIVO_LIBROS, ARCHIVO_PRESTAMOS, ARCHIVO_VECINOS, ARCHIVO_CONFIG]
for archivo_origen in archivos_a_respaldar:
if archivo_origen.exists():
archivo_destino = carpeta_respaldo / archivo_origen.name
shutil.copy2(archivo_origen, archivo_destino)
config['sistema']['ultimo_respaldo'] = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
print(f" ✅ Respaldo creado en: {carpeta_respaldo}")
except Exception as e:
print(f" ❌ Error al crear respaldo: {e}")
# ============================================================
# PROGRAMA PRINCIPAL
# ============================================================
if __name__ == "__main__":
print(" 🏛️ SISTEMA DE PRÉSTAMOS - BIBLIOTECA BARRIAL 📚")
config, libros, prestamos, vecinos = inicializar_sistema()
# Aquí iría el bucle while True del menú principal
print(" 👋 ¡Sistema listo para operar!")
3. Entendiendo la Arquitectura
- Separación de responsabilidades: Cada función hace una sola cosa y la hace bien (ej.
cargar_libros()solo carga, no registra). - Manejo de errores en cada nivel: Cada interacción con archivos tiene su propio
try/except. Si algo falla, el sistema no colapsa, se recupera con elegancia. - Datos en el formato correcto: JSON para datos complejos y anidados (libros, vecinos), CSV para datos tabulares (historial de préstamos),
pathlibpara rutas. - Configuración externa:
config.jsonpermite cambiar días de préstamo o multas sin tocar el código fuente.
- Olvidar
encoding="utf-8"al abrir archivos, lo que corrompe tildes y ñ. - No convertir las fechas de string a objetos
datetimeal leer CSV, impidiendo calcular días de retraso. - Modificar una lista mientras se itera sobre ella (usa copias o comprensiones de lista).
4. Más Allá del Curso: De Archivos a Bases de Datos
Lo que acabas de construir no es magia; es la base de todo sistema de datos. Cuando aprendas SQL, verás que:
SELECT * FROM libros WHERE autor = 'Juan León Mera'es lo mismo que tu funciónbuscar_libros().INSERT INTO vecinos...es lo mismo quevecinos.append()y guardar en JSON.- Las bases de datos son simplemente archivos muy bien organizados con índices, transacciones y optimizaciones. Ya tienes la mentalidad correcta.
El Reto: Expansión Comunitaria
Ahora que tienes el sistema base, es momento de personalizarlo y demostrar tu autonomía. Elige uno de los siguientes caminos y propón una solución (puede ser código modificado, un diagrama de flujo o un documento de diseño):
Adapta el sistema para una ferretería comunitaria. Agrega campos como "estado de mantenimiento", "número de serie" y "multa por daño".
Diseña un "Modo Simple" en el menú: opciones de texto más grande, comandos de voz simulados o menús reducidos para adultos mayores.
Agrega una función que lea el historial CSV y recomiende: "Los vecinos que pidieron este libro también pidieron...".
Evaluación Lúdica: Demuestra tu Maestría
Cargando pregunta...