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

  1. Por qué un proyecto integrador (y por qué ahora)
  2. Qué es Papyrus Online: el sistema completo
  3. Requisitos funcionales (RF1-RF7)
  4. Requisitos no funcionales (RNF1-RNF4)
  5. Criterios de aceptación medibles
  6. Alcance: lo que queda fuera (y por qué)
  7. Dos itinerarios: API Flask o web Django
  8. Mapa de requisitos → módulos del curso
  9. 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, como LUIS-001). Un socio identificado obtiene el 5 % de descuento (DESCUENTO_SOCIO = 0.05) sobre el precio con IVA. Un código inexistente debe rechazarse con SocioInvalidoError.
  • RF3 — Ventas con cupones y control de stock: vender N unidades de un título verifica stock (si falta, StockInsuficienteError y 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 el TransaccionCatalogo de 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 con ModelForm, 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.csv debe 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.log con nivel adecuado (INFO operaciones, WARNING rechazos de negocio, ERROR fallos 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 papyrus anotadas (parámetros y retorno), como aprendiste en 08-01.
  • RNF3 — Errores propios: ningún error de negocio se comunica con ValueError genérico ni con print; siempre la jerarquía ErrorPapyrus de M7. La interfaz los traduce a códigos HTTP o mensajes de usuario.
  • RNF4 — README: un README.md con 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 NOEXISTECuponInvalidoError.
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.

  1. Hito 0.1 — Elige itinerario y déjalo por escrito. Crea la carpeta del proyecto (papyrus_online/) con un fichero DECISIONES.md y 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.
  2. Hito 0.2 — Auditoría de piezas reutilizables. Recorre tu código de M1-M11 y lista en DECISIONES.md qué ficheros vas a reutilizar (con su origen) y cuáles harás de cero. Mínimo: revisa modelos.py, errores.py, cupones.py, almacen.py, tus tests de M9 y tu app de M10.
  3. 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:

  1. 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.»
  2. Reutilizables típicos: modelos.py (M5, añadiendo Socio), errores.py (M7, tal cual), cupones.py (M7, tal cual), aplicar_cupon y 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).
  3. 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

Módulo 2: Estructuras de Control

Módulo 3: Funciones y Módulos

Módulo 4: Estructuras de Datos

Módulo 5: Programación Orientada a Objetos

Módulo 6: Manejo de Archivos

Módulo 7: Manejo de Errores y Excepciones

Módulo 8: Temas Avanzados

Módulo 9: Pruebas y Depuración

Módulo 10: Desarrollo Web con Python

Módulo 11: Ciencia de Datos con Python

Módulo 12: Proyecto Final

© Copyright 2026. Todos los derechos reservados