Once módulos después de aquel print("Hola, Mundo"), tienes todas las piezas sobre la mesa: un paquete papyrus con modelos, errores y tests, dos formas de servirlo por la web y un laboratorio de datos que responde preguntas de negocio. Lo que no has hecho todavía es construir un sistema entero tú solo, desde una hoja de requisitos hasta un entregable. Ese salto — de seguir lecciones a decidir, implementar y verificar sin red — es exactamente lo que separa a quien "ha hecho un curso de Python" de quien "sabe construir con Python". El módulo 12 es tu examen final autodidacta: Papyrus Online, la versión integral de la librería de Ana. Esta primera lección define el contrato: qué se construye, cómo se mide que está bien, y qué queda deliberadamente fuera.
Contenido
- Por qué un proyecto integrador (y por qué ahora)
- Qué es Papyrus Online: el sistema completo
- Requisitos funcionales (RF1-RF7)
- Requisitos no funcionales (RNF1-RNF4)
- Criterios de aceptación medibles
- Alcance: lo que queda fuera (y por qué)
- Dos itinerarios: API Flask o web Django
- Mapa de requisitos → módulos del curso
- Cómo trabajar este módulo
Por qué un proyecto integrador
Hasta ahora, cada lección te daba el problema y la forma de resolverlo. En el trabajo real, nadie hace eso: recibes requisitos (a menudo vagos), y las decisiones — qué estructura, qué errores, qué probar — son tuyas. Un proyecto integrador entrena tres músculos que ninguna lección aislada puede entrenar:
- Decisión bajo ambigüedad: elegir entre alternativas válidas y asumir las consecuencias (itinerario A o B, dict o lista, JSON o CSV).
- Integración: las piezas que funcionan solas fallan juntas de formas nuevas — rutas relativas que se rompen, floats que no cuadran, imports circulares. Depurar entre capas es una habilidad distinta de depurar dentro de una función.
- Cierre: decidir cuándo algo está "hecho" y entregarlo. Los proyectos personales suelen morir al 80 %; este módulo te da criterios de aceptación explícitos para que el 100 % exista y sea alcanzable.
Las lecciones de este módulo no te darán todo el código. Te darán estructura, esqueletos, contratos y puntos de control; tú implementas. Donde una pieza integre demasiadas cosas a la vez (como el servicio de ventas), habrá solución completa comentada. Para el resto, soluciones orientativas: si la tuya es distinta pero pasa los criterios, es tan válida como la nuestra.
Qué es Papyrus Online
Papyrus Online es el sistema que Ana usaría de verdad: gestiona el catálogo y los socios, registra ventas aplicando tarifas y cupones, persiste todo en disco, lo expone por una interfaz web y genera cada mes un informe con datos y un gráfico.
flowchart LR
subgraph Interfaz
API["API Flask (itinerario A)<br/>o web Django (itinerario B)"]
end
subgraph Core["Paquete papyrus (dominio)"]
M["modelos: Libro, Socio"]
S["servicios: ventas, cupones"]
E["errores: jerarquía ErrorPapyrus"]
end
subgraph Persistencia
J["catalogo.json / socios.json"]
C["ventas.csv"]
end
subgraph Analisis["Análisis"]
I["informe mensual<br/>pandas + Matplotlib"]
end
API --> Core
Core --> Persistencia
Persistencia --> Analisis
Core -. "logging → datos/papyrus.log" .-> L[(papyrus.log)]
Fíjate en la dirección de las flechas: la interfaz usa el core, nunca al revés. Es la regla que M10 llamaba "la web traduce, el paquete resuelve", y en 12-02 la convertiremos en arquitectura.
Requisitos funcionales
Numerados para poder referirnos a ellos durante todo el módulo (en 12-04 cada uno tendrá su test):
- RF1 — Gestión de catálogo (CRUD): alta, consulta, modificación y baja de libros. Cada libro tiene título, autor, precio y stock. No puede haber dos libros con el mismo título (el título es la clave, como en todo el curso).
- RF2 — Socios y descuentos: registro de socios con código único (formato
NOMBRE-NNN, comoLUIS-001). Un socio identificado obtiene el 5 % de descuento (DESCUENTO_SOCIO = 0.05) sobre el precio con IVA. Un código inexistente debe rechazarse conSocioInvalidoError. - RF3 — Ventas con cupones y control de stock: vender N unidades de un título verifica stock (si falta,
StockInsuficienteErrory no se modifica nada), aplica tarifa de socio si procede y cupón si procede (PAPYRUS10→ 10 %,SOCIO5→ 5 %; cupón desconocido →CuponInvalidoError), descuenta stock y registra la venta. - RF4 — Persistencia: catálogo y socios en JSON; ventas en CSV con columnas
fecha,titulo,unidades,importe. El sistema arranca cargando los ficheros y no pierde datos si una escritura falla a medias (guardado atómico, como elTransaccionCatalogode M8). - RF5 — Interfaz (a elegir): itinerario A, API REST con Flask (
GET/POST/PUT/DELETE /api/libros,POST /api/ventas, errores 404/409/400 en JSON); itinerario B, web Django (listado, detalle, formulario de venta conModelForm, admin activo). - RF6 — Informe de datos mensual: a partir del CSV de ventas, un script genera: total de unidades e importe del mes, top 3 de títulos, comparativa por día de la semana, y un gráfico guardado como PNG. Con
ventas_2026.csvdebe reproducir los números canónicos de M11 (520 unidades ene-jun, sábado como mejor día con 130). - RF7 — Registro (logging): toda venta, error de negocio y operación de persistencia queda registrada en
datos/papyrus.logcon nivel adecuado (INFOoperaciones,WARNINGrechazos de negocio,ERRORfallos técnicos).
Requisitos no funcionales
- RNF1 — Tests: la lógica de negocio (RF1-RF4) cubierta con pytest. Mínimo exigible: la suite de 12-04 (unos 25 tests) en verde. Los criterios de aceptación de abajo son la especificación de esos tests.
- RNF2 — Type hints: todas las funciones públicas del paquete
papyrusanotadas (parámetros y retorno), como aprendiste en 08-01. - RNF3 — Errores propios: ningún error de negocio se comunica con
ValueErrorgenérico ni conprint; siempre la jerarquíaErrorPapyrusde M7. La interfaz los traduce a códigos HTTP o mensajes de usuario. - RNF4 — README: un
README.mdcon qué es el proyecto, cómo instalarlo (venv +requirements.txt), cómo usarlo y cómo ejecutar los tests. Plantilla completa en 12-05.
Criterios de aceptación medibles
"Funciona" no es un criterio; esto sí. Cada fila es verificable con una acción concreta:
| Requisito | Criterio de aceptación (verificable) |
|---|---|
| RF1 | Dar de alta "Fausto" dos veces produce error (409 en API / mensaje en form); tras borrar un libro, consultarlo devuelve LibroNoEncontradoError (404). |
| RF2 | Vender La Odisea a LUIS-001 cobra 12.35 €; a Júlia (sin socio), 13.00 €; con código PEPE-999, SocioInvalidoError. |
| RF3 | Vender 5 Odiseas con stock 4 lanza StockInsuficienteError y el stock sigue siendo 4; Fausto + socio + PAPYRUS10 cobra 18.67 €; cupón NOEXISTE → CuponInvalidoError. |
| RF4 | Tras vender y reiniciar el programa, el stock leído de catalogo.json refleja la venta; matar el proceso durante un guardado no deja un JSON corrupto. |
| RF5 | A: los 6 endpoints responden con los códigos correctos (probado con el test client). B: listado, detalle, venta y admin operativos (probado con Client). |
| RF6 | Ejecutar el informe sobre ventas_2026.csv imprime 520 unidades totales y sábado como mejor día (130 u), y deja informe_2026.png en disco. |
| RF7 | Tras una venta rechazada por stock, datos/papyrus.log contiene una línea WARNING con el título y las unidades pedidas. |
| RNF1-4 | pytest en verde; mypy (o revisión manual) sin funciones públicas sin anotar; búsqueda de except Exception genéricos sin justificar da cero; el README permite a otra persona arrancar el proyecto sin preguntarte nada. |
Los importes salen de la fórmula de todo el curso: precio × 1.04 (IVA) × 0.95 (socio), redondeado a 2 decimales, y el cupón se aplica sobre ese resultado. Verifícalo con los cuatro libros canónicos: La Odisea 12.50 → 13.00 / 12.35; Hamlet 9.95 → 10.35 / 9.83; El Quijote 15.90 → 16.54 / 15.71; Fausto 21.00 → 21.84 / 20.75.
Alcance: lo que queda fuera
Tan importante como el qué es el qué no. Decidir alcance es ingeniería; querer hacerlo todo es la forma más segura de no terminar nada.
| Fuera del alcance | Por qué | Dónde retomarlo |
|---|---|---|
| Autenticación real (usuarios, contraseñas, sesiones) | Requiere seguridad seria (hashing, CSRF, tokens); mal hecha es peor que ausente | Extensiones en 12-05 |
| Pagos | Integración con pasarelas externas, fuera del temario | Extensiones en 12-05 |
| Despliegue a producción | Servidores, HTTPS, dominios: otro oficio | Extensiones en 12-05 |
| Base de datos SQL en el itinerario A | JSON/CSV bastan para el volumen de Papyrus y son lo practicado en M6 | Extensiones en 12-05 |
| Front-end elaborado (JavaScript, CSS avanzado) | El curso es de Python; HTML mínimo es suficiente | Extensiones en 12-05 |
Si al terminar te quedas con ganas, perfecto: 12-05 ordena estas extensiones por dificultad. Pero primero se entrega el alcance acordado.
Dos itinerarios
RF5 se cumple por uno de dos caminos. Elige según lo que quieras reforzar, no según cuál "suena mejor":
| Itinerario A — API Flask | Itinerario B — web Django | |
|---|---|---|
| Qué construyes | API REST JSON sobre tu paquete papyrus |
Aplicación web con plantillas, forms y admin |
| Base del curso | 10-02 y 10-03 | 10-04 y 10-05 |
| Persistencia | Tus repositorios JSON/CSV (RF4 íntegro) | ORM de Django para el catálogo; ventas siguen en CSV |
| Esfuerzo relativo | Menor en interfaz, mayor en persistencia (todo es tuyo) | Mayor en interfaz (plantillas, forms), menor en persistencia (ORM regala mucho) |
| Refuerza | Diseño de APIs, códigos HTTP, separación de capas | Frameworks completos, convenciones, ORM |
| Riesgo típico | Olvidar validar la entrada JSON | Meter lógica de negocio en las vistas |
| Elige si… | Te tira el backend puro o el trabajo con datos | Te tira el desarrollo web de producto |
Ambos itinerarios comparten el 70 % del proyecto (todo el core, la persistencia de ventas, el informe y los tests de negocio). La decisión afecta solo al hito H4 de la planificación.
Mapa de requisitos → módulos del curso
Nada de este proyecto es nuevo. Cada requisito apunta a módulos donde ya lo aprendiste — esta tabla es tu chuleta de "dónde repasar":
| Requisito | Qué necesita | Módulo(s) donde se aprendió |
|---|---|---|
| RF1 catálogo | dataclass Libro, diccionarios como índice, funciones del almacén |
M5 (05-06), M4 (04-03), M3 |
| RF2 socios | Validación, SocioInvalidoError, constantes |
M2, M7 (07-04), M1 |
| RF3 ventas y cupones | Condicionales, excepciones propias, aplicar_cupon, redondeo |
M2, M7, M3, M1 (floats) |
| RF4 persistencia | JSON, CSV, pathlib, guardado atómico con context manager |
M6, M8 (08-04) |
| RF5-A API Flask | Rutas, JSON, códigos de estado, errorhandler |
M10 (10-02, 10-03) |
| RF5-B web Django | Modelos, vistas, plantillas, ModelForm, admin |
M10 (10-04, 10-05) |
| RF6 informe | pandas (groupby), Matplotlib (savefig) |
M11 (11-03, 11-04) |
| RF7 logging | logging con niveles y fichero |
M7 (07-05) |
| RNF1 tests | pytest, fixtures, parametrize, tmp_path |
M9 (09-01 a 09-03) |
| RNF2 type hints | Anotaciones como especificación | M8 (08-01) |
| RNF3 errores | Jerarquía ErrorPapyrus |
M7 (07-04) |
| RNF4 README | Entornos virtuales, pip, requirements | M1 (01-06) |
¿Y M11 completo, con su regresión? Reaparece en 12-05: el informe final incluirá una recomendación al estilo del pedido de Sant Jordi (predicción 172, pedido de Ana 190 — el modelo recomienda, la persona decide).
Cómo trabajar este módulo
Las cinco lecciones siguen el ciclo de vida real de un proyecto: requisitos (esta), diseño (12-02), construcción (12-03), verificación (12-04) y entrega (12-05). Trabaja con el editor abierto: cada lección termina con hitos que debes completar antes de la siguiente. Reutiliza tu código de módulos anteriores sin vergüenza — reutilizar lo propio es lo que hacen los profesionales — pero cópialo a un proyecto nuevo y límpialo al pasarlo: el proyecto final merece empezar en una carpeta limpia.
Errores Comunes y Consejos
- Empezar a programar hoy mismo. La tentación es enorme y el error, clásico: sin el diseño de 12-02 acabarás reescribiendo. Un día de diseño ahorra tres de refactor.
- Ampliar el alcance a mitad de camino ("ya que estoy, le pongo login…"). Se llama scope creep y mata más proyectos personales que cualquier bug. Apunta la idea para 12-05 y sigue.
- Elegir el itinerario B "porque Django es más profesional". Ninguno es más profesional; son oficios distintos. Elige por lo que quieras reforzar.
- Tratar los criterios de aceptación como decorativos. Son el contrato: en 12-04 se convierten uno a uno en tests. Si un criterio te parece ambiguo ahora, redáctalo mejor ahora — es mucho más barato que descubrir la ambigüedad con el código escrito.
- Saltarse los importes canónicos. 12.35, 9.83, 15.71, 20.75 llevan todo el curso siendo la verdad. Si tu sistema da otro número, el bug es tuyo, no de la tabla.
Ejercicios
Este módulo sustituye los ejercicios de práctica por hitos de proyecto: trabajo real sobre tu entregable, con criterios de verificación.
- Hito 0.1 — Elige itinerario y déjalo por escrito. Crea la carpeta del proyecto (
papyrus_online/) con un ficheroDECISIONES.mdy escribe 3-5 líneas: qué itinerario eliges y por qué, en términos de qué quieres reforzar. Este fichero crecerá durante todo el módulo. - Hito 0.2 — Auditoría de piezas reutilizables. Recorre tu código de M1-M11 y lista en
DECISIONES.mdqué ficheros vas a reutilizar (con su origen) y cuáles harás de cero. Mínimo: revisamodelos.py,errores.py,cupones.py,almacen.py, tus tests de M9 y tu app de M10. - Hito 0.3 — Un criterio más. Redacta un criterio de aceptación adicional para el requisito que peor entiendas, con el formato de la tabla (acción concreta → resultado verificable). Redactarlo te obligará a entender el requisito.
Soluciones
Orientativas — lo importante es que existan y sean concretas:
- Ejemplo válido: «Itinerario A. En M10 Django me resultó más guiado y Flask me dejó más dudas sobre códigos HTTP y validación de entrada; quiero cerrar esas dudas. Además el itinerario A me obliga a hacer la persistencia entera, que es donde menos seguro me siento.»
- Reutilizables típicos:
modelos.py(M5, añadiendoSocio),errores.py(M7, tal cual),cupones.py(M7, tal cual),aplicar_cupony constantes de M1; de cero o muy retocados:almacen.py(pasará a servicios + repositorios en 12-02), la interfaz (se rehace sobre el diseño nuevo), el informe (guion nuevo sobre lo aprendido en M11). - Ejemplo para RF4: «Ejecuto una venta, mato el proceso, vuelvo a arrancar: el catálogo carga sin error y el stock es coherente (o la venta completa se aplicó, o no se aplicó nada — nunca a medias).» Si tu criterio no dice cómo verificarlo, no es un criterio: es un deseo.
Conclusión
Ya tienes el contrato: siete requisitos funcionales, cuatro no funcionales, criterios de aceptación que se pueden comprobar (y que en 12-04 se convertirán en tests), un alcance con fronteras claras y dos itinerarios entre los que ya has elegido. También has visto que no hay nada nuevo que aprender: cada requisito apunta a un módulo donde ya viviste la solución. Lo nuevo es el ensamblaje — y el ensamblaje empieza por el plano. En la próxima lección convertiremos estos requisitos en un plan: hitos ordenados, arquitectura en capas, esquemas de datos y contratos de funciones escritos antes de programar. Resiste un día más la tentación del editor: primero el plano, luego los ladrillos.
Curso de Programación en Python
Módulo 1: Introducción a Python
- Introducción a Python
- Configuración del Entorno de Desarrollo
- Sintaxis de Python y Tipos de Datos Básicos
- Variables y Constantes
- Entrada y Salida Básica
- Entornos Virtuales y Gestión de Paquetes
Módulo 2: Estructuras de Control
- Sentencias Condicionales
- Bucles: for y while
- Herramientas de Control de Flujo
- Comprensiones de Listas
Módulo 3: Funciones y Módulos
- Definición de Funciones
- Argumentos de Función
- Funciones Lambda
- Módulos y Paquetes
- Visión General de la Biblioteca Estándar
Módulo 4: Estructuras de Datos
Módulo 5: Programación Orientada a Objetos
Módulo 6: Manejo de Archivos
- Lectura y Escritura de Archivos
- Trabajo con Archivos CSV
- Manejo de Datos JSON
- Operaciones con Archivos y Directorios
Módulo 7: Manejo de Errores y Excepciones
- Introducción a las Excepciones
- Manejo de Excepciones
- Lanzamiento de Excepciones
- Excepciones Personalizadas
- Buenas Prácticas y Registro de Errores con logging
Módulo 8: Temas Avanzados
- Anotaciones de Tipos (type hints)
- Decoradores
- Generadores
- Administradores de Contexto
- Concurrencia: Hilos y Procesos
- Asyncio para Programación Asíncrona
Módulo 9: Pruebas y Depuración
- Introducción a las Pruebas
- Pruebas Unitarias con unittest
- Pruebas con pytest
- Desarrollo Guiado por Pruebas
- Técnicas de Depuración
- Uso de pdb para Depuración
Módulo 10: Desarrollo Web con Python
- Introducción al Desarrollo Web
- Fundamentos del Framework Flask
- Construcción de APIs REST con Flask
- Introducción a Django
- Construcción de Aplicaciones Web con Django
Módulo 11: Ciencia de Datos con Python
- Introducción a la Ciencia de Datos
- NumPy para Computación Numérica
- Pandas para Manipulación de Datos
- Matplotlib para Visualización de Datos
- Introducción al Aprendizaje Automático con scikit-learn
