Evaluación 3

Prototipo Web Funcional

Entrega Incremental 3 - FastAPI + HTMX + Bootstrap

20%
Prototipo
Fecha límite: 26 de marzo de 2026
Requisitos Previos

REQUIERE E1 (Dominio) y E2 (Testing) completadas. Se expondrá el mismo dominio probado vía web. Los tests de E2 deben pasar.

Clases Relacionadas

Estas clases preparan los conocimientos necesarios para esta evaluación:

Objetivo de Aprendizaje

Convertir el dominio de TaskFlow en una aplicación web interactiva usando FastAPI (backend) y HTMX (frontend dinámico sin JavaScript complejo).

Modalidad de Trabajo

Individual o en parejas (máximo 2 personas). Debe ser el mismo equipo de E1-E2.

Formato de entrega: Repositorio GitHub con: dominio + tests + API FastAPI + templates Jinja2. Incluir requirements.txt y README.md con instrucciones para ejecutar con uvicorn.

Requisitos Técnicos Detallados

1. API REST con FastAPI

Descripción: Implementar endpoints CRUD para todas las entidades del dominio.

Endpoints obligatorios:
Status codes correctos:
Documentación automática:
  • /docs debe mostrar Swagger UI funcional
  • Todos los endpoints con descripción y ejemplos
  • Pydantic models para request/response bodies
2. Modelos Pydantic

Descripción: Validación automática de datos con Pydantic.

Modelos obligatorios:
Validaciones con Field:
3. Frontend con HTMX

Descripción: Interfaz web dinámica sin JavaScript complejo, usando atributos HTMX.

Funcionalidades obligatorias:
Atributos HTMX a usar:
Sin JavaScript personalizado: Toda la interactividad debe ser con HTMX. No escribir funciones JS para lo que HTMX ya resuelve.
4. Templates Jinja2

Descripción: Renderizar HTML con Jinja2, no strings en Python.

Templates obligatorios:

Ejemplo de Implementación

api/main.py
from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel, Field, EmailStr
from typing import Optional
from src.domain.usuario import Usuario
from src.domain.proyecto import Proyecto
from src.domain.tarea import Tarea, PrioridadTarea, EstadoTarea

app = FastAPI(title="TaskFlow API", version="1.0.0")
templates = Jinja2Templates(directory="templates")

# Almacenamiento en memoria (se reemplazará en E4)
usuarios_db: dict[int, Usuario] = {}
proyectos_db: dict[int, Proyecto] = {}


class UsuarioCreate(BaseModel):
    """Modelo para crear usuario."""
    username: str = Field(..., min_length=3, max_length=50)
    email: EmailStr
    nombre_completo: Optional[str] = None


class UsuarioResponse(BaseModel):
    """Modelo de respuesta de usuario."""
    id: int
    username: str
    email: str
    activo: bool

    class Config:
        from_attributes = True


@app.get("/", response_class=HTMLResponse)
async def home(request: Request):
    """Página principal."""
    return templates.TemplateResponse(
        "index.html",
        {"request": request, "proyectos": list(proyectos_db.values())}
    )


@app.get("/proyectos", response_class=HTMLResponse)
async def lista_proyectos(request: Request):
    """Lista de proyectos (parcial para HTMX)."""
    return templates.TemplateResponse(
        "proyectos/lista.html",
        {"request": request, "proyectos": list(proyectos_db.values())}
    )


@app.post("/proyectos", response_model=dict)
async def crear_proyecto(data: ProyectoCreate):
    """Crear nuevo proyecto."""
    nuevo_id = len(proyectos_db) + 1
    lider = usuarios_db.get(data.lider_id)
    if not lider:
        raise HTTPException(status_code=404, detail="Líder no encontrado")

    proyecto = Proyecto(
        nombre=data.nombre,
        descripcion=data.descripcion,
        lider=lider
    )
    proyectos_db[nuevo_id] = proyecto
    return {"id": nuevo_id, "mensaje": "Proyecto creado"}
templates/tareas/item.html
<div class="list-group-item d-flex justify-content-between align-items-center"
     id="tarea-{{ tarea.id }}">
    <div>
        <span class="badge bg-{{ prioridad_color }} me-2">
            {{ tarea.prioridad.name }}
        </span>
        <span class="{% if tarea.estado == 'completada' %}text-decoration-line-through text-muted{% endif %}">
            {{ tarea.titulo }}
        </span>
    </div>

    {% if tarea.estado != 'completada' %}
    <button class="btn btn-sm btn-success"
            hx-patch="/tareas/{{ tarea.id }}/completar"
            hx-target="#tarea-{{ tarea.id }}"
            hx-swap="outerHTML">
        <i class="bi bi-check-lg"></i> Completar
    </button>
    {% endif %}
</div>

Estructura de Entrega

Organización de Archivos Obligatoria
src/ # Dominio (de E1) └── domain/ # Sin cambios api/ # Backend FastAPI ├── __init__.py ├── main.py # App FastAPI + rutas ├── models.py # Modelos Pydantic └── routes/ ├── __init__.py ├── usuarios.py # Rutas de usuarios ├── proyectos.py # Rutas de proyectos └── tareas.py # Rutas de tareas templates/ # Templates Jinja2 ├── base.html # Layout base con Bootstrap ├── index.html # Página principal ├── proyectos/ │ ├── lista.html # Lista de proyectos │ └── form.html # Formulario crear └── tareas/ ├── lista.html # Lista parcial HTMX └── item.html # Item individual HTMX static/ # CSS/JS personalizado (mínimo) └── css/ └── custom.css # Estilos adicionales requirements.txt # fastapi, uvicorn, jinja2 README.md # Cómo ejecutar el servidor

Pasos para Completar la Evaluación

1Instalar FastAPI y dependencias

pip install fastapi uvicorn jinja2 python-multipart

2Crear modelos Pydantic

UsuarioCreate, ProyectoCreate, TareaCreate, Response models

3Implementar endpoints de usuarios

GET lista, POST crear, GET por ID

4Implementar endpoints de proyectos

CRUD completo + agregar tareas

5Implementar endpoints de tareas

Completar y cambiar prioridad

6Crear templates Jinja2

Base layout, listas, formularios

7Agregar HTMX a templates

Atributos hx-get, hx-post, hx-target en botones y forms

Rúbrica de Evaluación (100%)

Criterio Excelente (5.0) Aceptable (3.5) Insuficiente Peso
API REST Todos los endpoints funcionan, status codes correctos, /docs completo Endpoints básicos, algunos status incorrectos API incompleta o no funciona 30%
Frontend HTMX Sin recargas, hx-post/get/patch bien usados, UX fluida Algunas recargas, HTMX parcial Sin HTMX o JavaScript puro 25%
Templates Jinja2 Templates organizados, herencia base.html, sin HTML en Python Templates básicos sin herencia HTML como strings en Python 20%
Diseño Bootstrap Responsivo, profesional, formularios y tablas bien estilados Diseño funcional pero básico Sin Bootstrap o diseño roto 15%
Integración E1+E2 Usa dominio real de E1, tests de E2 pasan Dominio copiado/modificado No usa el dominio de E1 10%
Penalizaciones
Conexión con E4

El prototipo que construyas aquí perderá datos al reiniciar. En E4: Persistencia agregarás almacenamiento en archivos JSON.

← E2: Testing E4: Persistencia →