Integrar el inventario y el intercambio de archivos

Ciencia y tecnologíaIntegrar el inventario y el intercambio de archivos

Ya tienes las reglas, el almacenamiento y el diseño de la terminal. Hoy unirás esas piezas. La carpeta proyecto contendrá modelo.py del capítulo catorce, almacen.py de los capítulos quince y dieciséis, intercambio.py y app.py. Estos dos últimos se ofrecen completos aquí. Los archivos de práctica anteriores no forman parte de la aplicación.

Comprobar las piezas antes de unirlas

Revisa que almacen.py contiene conectar, agregar_varios y consultar. No copies dos versiones de la misma función. El módulo modelo debe ofrecer Articulo y crear_articulo. Ejecuta sus pruebas antes de continuar. Un error en una regla pequeña resulta más fácil de localizar ahora que cuando esté mezclado con argumentos y archivos.

El programa final conservará el alcance acordado: alta, consulta e intercambio de artículos. Una importación no actualiza códigos existentes. Los rechaza y revierte su lote. Una exportación crea un archivo nuevo. No incluye edición ni borrado, lo que permite practicar con una aplicación terminada sin introducir operaciones adicionales que todavía no hemos definido.

Crear intercambio.py

Este módulo comprueba el formato antes de entregar artículos al almacenamiento. El límite de dos millones de bytes y diez mil registros es una decisión educativa para trabajar con lotes pequeños. La comprobación de tamaño presupone un archivo local que no cambia mientras se lee. No es un mecanismo para procesar entradas hostiles ni archivos compartidos en modificación.

from pathlib import Path
import csv

CAMPOS = ["codigo", "nombre", "cantidad"]

def leer_csv(ruta): ruta = Path(ruta) if ruta.stat().st_size > 2000000: raise ValueError("El CSV supera el límite de 2 MB decimales") articulos = [] vistos = set() try: with ruta.open("r", encoding="utf-8-sig", newline="") as archivo: lector = csv.DictReader(archivo, delimiter=";", strict=True) if lector.fieldnames != CAMPOS: raise ValueError("Cabecera esperada: codigo;nombre;cantidad") for numero, fila in enumerate(lector, start=1): if numero > 10000: raise ValueError("Máximo de 10000 registros por archivo") if None in fila or any(v is None for v in fila.values()): raise ValueError(f"Registro {numero}: columnas incorrectas") try: articulo = crear_articulo(**fila) except ValueError as error: raise ValueError(f"Registro {numero}: {error}") from error if articulo.codigo in vistos: raise ValueError(f"Registro {numero}: código repetido") vistos.add(articulo.codigo) articulos.append(articulo) except csv.Error as error: raise ValueError(f"Formato CSV incorrecto: {error}") from error return articulos

def escribir_csv(ruta, articulos): with Path(ruta).open("x", encoding="utf-8", newline="") as archivo: escritor = csv.DictWriter(archivo, fieldnames=CAMPOS, delimiter=";") escritor.writeheader() for articulo in articulos: escritor.writerow({ "codigo": articulo.codigo, "nombre": articulo.nombre, "cantidad": articulo.cantidad, }) «`

Crear app.py

Copia este bloque en un archivo nuevo con ese nombre. Las opciones globales van antes del comando. main acepta una lista opcional de argumentos para facilitar futuras pruebas. Si no se proporciona, argparse utiliza los de la terminal. La conexión se cierra mediante finally incluso cuando una operación produce una excepción.

from pathlib import Path
import argparse
import sqlite3
import sys
from modelo import crear_articulo
from almacen import conectar, agregar_varios, consultar

def crear_parser(): parser = argparse.ArgumentParser(description="Inventario local del curso Python") parser.add_argument("–db", default="inventario.db", help="Ruta de la base") sub = parser.add_subparsers(dest="comando", required=True) sub.add_parser("iniciar", help="Crear o preparar la base") alta = sub.add_parser("alta", help="Añadir un artículo") alta.add_argument("codigo") alta.add_argument("nombre") alta.add_argument("cantidad") listar = sub.add_parser("listar", help="Mostrar artículos") listar.add_argument("–buscar", default="") importar = sub.add_parser("importar", help="Importar un CSV completo") importar.add_argument("origen") exportar = sub.add_parser("exportar", help="Crear un CSV nuevo") exportar.add_argument("destino") return parser

def main(argv=None): args = crear_parser().parse_args(argv) ruta = Path(args.db) try: if args.comando != "iniciar" and not ruta.is_file(): raise FileNotFoundError("La base no existe. Usa iniciar con esa ruta") conexion = conectar(ruta) try: if args.comando == "iniciar": print("Base preparada:", ruta.resolve()) elif args.comando == "alta": articulo = crear_articulo(args.codigo, args.nombre, args.cantidad) agregar_varios(conexion, [articulo]) print("Artículo registrado:", articulo.codigo) elif args.comando == "listar": articulos = consultar(conexion, args.buscar) for articulo in articulos: print(f"{articulo.codigo} | {articulo.nombre} | {articulo.cantidad}") if not articulos: print("No hay artículos que mostrar") elif args.comando == "importar": articulos = leer_csv(args.origen) agregar_varios(conexion, articulos) print("Artículos importados:", len(articulos)) elif args.comando == "exportar": escribir_csv(args.destino, consultar(conexion)) print("CSV creado:", Path(args.destino).resolve()) finally: conexion.close() except sqlite3.IntegrityError: print("Error: código ya existente o restricción incumplida; lote revertido", file=sys.stderr) return 1 except (OSError, ValueError, sqlite3.Error) as error: print(f"Error: {error}", file=sys.stderr) return 1 return 0

if __name__ == "__main__": raise SystemExit(main()) «`

Recorrer una operación completa

Ejecuta python app.py iniciar desde proyecto. Después añade un artículo con python app.py alta CU001 "Cuaderno azul" 3. La terminal entrega textos, crear_articulo los normaliza y valida, agregar_varios inserta mediante parámetros y la transacción confirma. Finalmente la interfaz muestra el código registrado y cierra la conexión.

Consulta con python app.py listar –buscar CUAD. Después exporta con python app.py exportar salida-01.csv. Si repites la exportación al mismo destino fallará sin sustituir el archivo existente. Un fallo durante una escritura nueva podría dejar una salida parcial: no la consideres válida si la orden no termina correctamente. Utiliza otro destino después de corregir el problema.

Importar sin confundir origen y destino

Para ensayar una vuelta completa crea otra base con python app.py –db segunda.db iniciar. Importa allí salida-01.csv mediante python app.py –db segunda.db importar salida-01.csv. No lo importes en la primera base esperando que actualice el artículo: el código ya existe y el contrato lo rechaza. Compara ambas consultas para comprobar que el intercambio conserva los datos.

Mantén cerrados los archivos de prueba mientras los importas. Utiliza rutas explícitas si cambias de carpeta. La aplicación no ofrece coordinación entre varias personas ni protege contra modificaciones externas del esquema. Una base existente debe proceder de este mismo proyecto. El comando iniciar prepara una tabla pero no migra versiones antiguas ni verifica cualquier archivo ajeno.

Errores habituales

Si aparece ModuleNotFoundError comprueba que los cuatro archivos están juntos y que sus nombres coinciden. Si una cantidad inválida llega al almacenamiento revisa que la entrada haya pasado por crear_articulo. Si una consulta parece vacía verifica la ruta absoluta de la base antes de sospechar que se borraron datos.

No añadas un except general para hacer desaparecer fallos que no comprendes. Los errores previstos ya tienen una frontera de presentación. Un defecto de programación debe quedar visible durante la práctica. Guarda una versión funcional de estos cuatro módulos antes de comenzar mejoras para poder comparar cada cambio con una referencia conocida.

Lo que deberías recordar

  • Los cuatro módulos conservan responsabilidades separadas.
  • La importación valida antes de insertar.
  • Los códigos existentes no se actualizan automáticamente.
  • Una exportación fallida puede dejar un archivo parcial.

Ejercicio práctico

Completa los cuatro archivos y ejecuta la ayuda. Inicializa una base nueva, añade dos artículos y consulta uno por una parte de su nombre. Exporta a un destino nuevo e importa ese CSV en una segunda base vacía. Compara códigos, nombres y cantidades de ambas.

Repite el alta de un código existente y comprueba que no se añade otro registro. Introduce una cantidad negativa y verifica que se rechaza antes de guardar. Intenta exportar sobre la salida ya creada y confirma que su contenido permanece igual.

Anota cada orden, su resultado esperado y su resultado real. No des por terminado el ejercicio solo porque una ejecución funciona. La entrega de esta lección es el recorrido completo con los casos correctos y tres errores previstos, además de los archivos conservados para las pruebas de aceptación del siguiente capítulo.

Siguiente: Verificar el proyecto y ampliarlo con método.

Volver al índice de Programar con Python.

Últimos posts

DEJA UNA RESPUESTA

Por favor ingrese su comentario!
Por favor ingrese su nombre aquí

 

Artículos más vistos

Horóscopo diario
Menú diario