Plataforma de revisión técnica de productos de hormigón
Contexto
En el laboratorio, los productos de hormigón llegan como texto libre: descripciones no estructuradas que los técnicos tienen que revisar, clasificar y validar antes de emitir un informe. El proceso era manual, lento y propenso a errores de duplicación: dos técnicos podían estar revisando el mismo producto al mismo tiempo sin saberlo.
El objetivo era construir una plataforma web que centralizara la revisión, asignara productos automáticamente a los técnicos disponibles y garantizara que ningún producto fuera revisado dos veces en paralelo.
El problema
Tres problemas concretos a resolver:
- Concurrencia: varios técnicos trabajan en paralelo. Sin control, dos personas podían revisar el mismo producto simultáneamente y sobrescribirse.
- Extracción de datos: los productos venían como texto libre. Había que extraer campos estructurados (tipo, resistencia, dosificación) usando regex antes de que el técnico los validara.
- Trazabilidad: necesitaban saber quién revisó qué, cuándo y qué cambios hizo.
Decisiones de arquitectura
Por qué Flask y no FastAPI
El proyecto partió cuando FastAPI era menos maduro en el ecosistema del laboratorio. Flask con Jinja2 permitía construir la interfaz completa (backend + templates HTML) sin introducir un frontend JavaScript separado. Para una audiencia de ~10 técnicos internos, esa simplicidad era una ventaja, no una limitación.
Por qué Redis para control de concurrencia
flowchart TD
A([Técnico abre producto]) --> B{"Redis\nSET NX EX"}
B -- "key libre" --> C["Producto asignado\nal técnico"]
B -- "key ocupada" --> D["Siguiente producto\ndisponible"]
C --> E{"Fin de sesión"}
E -- "técnico guarda" --> F(["Redis DEL\nlibera bloqueo"])
E -- "TTL expira" --> F
Necesitaba un mecanismo de bloqueo distribuido: cuando un técnico abre un producto para revisarlo, ese producto queda “bloqueado” para los demás hasta que lo libera o expira el timeout.
Redis resolvió esto con dos primitivas:
- SET NX EX: crea el bloqueo atómicamente solo si no existe. Si otro técnico ya tiene el producto, la operación falla y el sistema asigna otro.
- TTL automático: si un técnico cierra el navegador sin guardar, el bloqueo expira solo en X segundos. Sin esto, los productos quedan bloqueados indefinidamente.
Evalué usar la base de datos principal para los bloqueos (con transacciones y locks). Lo descarté: Redis es órdenes de magnitud más rápido para operaciones de este tipo y su modelo de expiración automática resuelve el problema del técnico desconectado de forma elegante.
Procesamiento con regex
Antes de presentar un producto al técnico, el sistema aplica un conjunto de extractores regex que intentan parsear automáticamente los campos estructurados. El técnico ve el resultado pre-llenado y solo corrige lo que el regex no capturó bien.
Esto redujo el tiempo de revisión por producto: en vez de tipear todo desde cero, el técnico valida y ajusta.
Stack
- Python 3.x como lenguaje base
- Flask para el servidor web y templates
- Redis para control de concurrencia y bloqueos distribuidos
- Gunicorn para producción (múltiples workers)
- Regex avanzados para extracción de campos
- Exportación Excel con openpyxl
Estado del proyecto
El sistema fue construido, desplegado y usado en producción durante su ciclo de vida. Hoy está finalizado: el proceso que automatizaba cambió de flujo en el laboratorio. Es un caso frecuente en proyectos internos — el éxito a veces consiste en resolver el problema tan bien que el proceso subyacente se transforma.
Aprendizajes
1. El bloqueo distribuido es más fácil de diseñar que de comunicar. La lógica técnica de Redis SET NX EX es simple. Lo difícil fue diseñar la experiencia del técnico cuando un producto estaba bloqueado: ¿mensaje de error? ¿asignación automática del siguiente? ¿contador de espera? La decisión de UX importa tanto como la técnica.
2. Los regex son frágiles por diseño. Funcionan bien en el 80% de los casos. El 20% restante siempre encuentra una variante que el patrón no anticipó. La solución correcta no es más regex sino una interfaz que haga fácil corregir los casos que el regex falla — que fue exactamente lo que construimos.
3. “Finalizado” no es fracaso. Un sistema interno que cumple su propósito y luego el proceso evoluciona más allá de él es un resultado válido. El criterio de éxito no es que dure para siempre, sino que resuelva el problema mientras existe.