josedario87 8ec394a74e Fix: Usar clock_timestamp() en trigger updated_at para garantizar timestamps únicos
- Cambiar now() por clock_timestamp() en función set_updated_at()
- now() retorna el timestamp del inicio de la transacción
- clock_timestamp() retorna el timestamp actual del sistema
- Garantiza que cada UPDATE tenga un timestamp diferente
- Mejora la precisión para auditoría de cambios
2025-10-17 17:56:51 -06:00

rioCata - Sistema de Catación de Café

Sistema digital para realizar sesiones de catación de café de manera ordenada y profesional.

Arquitectura

  • Base de datos: PostgreSQL 16 con extensiones para JSONB y arrays
  • Containerización: Docker Compose
  • Estructura de datos: Modelo relacional optimizado para catación profesional

Estructura del Proyecto

rioCata/
├── docker-compose.yml          # Configuración de servicios
├── scripts/                    # Scripts de gestión y administración
│   ├── riocata.sh              # Script helper principal
│   └── README.md               # Documentación de scripts
├── postgres/
│   ├── init/                   # Scripts de inicialización (ejecutados automáticamente)
│   │   ├── 01_schema.sql      # Tipos y tablas
│   │   ├── 02_functions.sql   # Funciones y triggers
│   │   ├── 03_indexes.sql     # Índices de optimización
│   │   └── 04_sample_data.sql # Datos de prueba
│   └── tests/                  # Scripts de testing
│       ├── test_all.sql        # Suite completa de tests
│       └── example_queries.sql # Queries de ejemplo
└── README.md

Inicio Rápido

1. Levantar el entorno

docker-compose up -d

Esto iniciará PostgreSQL con todos los scripts de inicialización ejecutados automáticamente.

2. Verificar que el servicio está corriendo

docker-compose ps

Deberías ver riocata_postgres en estado Up.

3. Conectarse a la base de datos

docker-compose exec postgres psql -U riocata_user -d riocata

4. Ejecutar los tests

Una vez dentro de psql:

\i postgres/tests/test_all.sql

Esto ejecutará 18 tests que validan:

  • Existencia de tablas y tipos
  • Funcionamiento de triggers
  • Constraints de validación
  • Queries típicas
  • Funciones auxiliares

5. Explorar los datos de ejemplo

\i postgres/tests/example_queries.sql

Esto ejecutará 17 queries de ejemplo que muestran:

  • Listado de sesiones, muestras y evaluaciones
  • Top evaluaciones por puntaje
  • Evaluaciones con defectos
  • Promedios de parámetros por muestra
  • Análisis de notas de fragancia y sabor
  • Estadísticas por catador

Scripts Helper

Para facilitar la gestión del proyecto, hay scripts helper disponibles en la carpeta scripts/.

Script Principal: riocata.sh

El script scripts/riocata.sh proporciona comandos útiles para gestión:

# Ver ayuda
./scripts/riocata.sh help

# Iniciar servicios
./scripts/riocata.sh start

# Conectarse a PostgreSQL
./scripts/riocata.sh psql

# Ejecutar tests
./scripts/riocata.sh test

# Ejecutar queries de ejemplo
./scripts/riocata.sh queries

# Ver estado
./scripts/riocata.sh status

# Crear backup
./scripts/riocata.sh backup

# Restaurar backup
./scripts/riocata.sh restore backup_20251017_143022.sql

# Detener servicios
./scripts/riocata.sh stop

# Reiniciar todo (borra datos - ¡CUIDADO!)
./scripts/riocata.sh reset

Documentación completa: Ver scripts/README.md para información detallada sobre todos los comandos disponibles.

Modelo de Datos

Tablas Principales

sesion

Representa una sesión de catación.

  • id: UUID (PK)
  • codigo: Código único (ej. "S-2025-10-17-01")
  • fecha: Fecha de la catación
  • nombre: Nombre descriptivo (ej. "Mesa Laboratorio #1")

auth.users

Usuarios/catadores del sistema (simula Supabase auth.users).

  • id: UUID (PK)
  • email: Email único
  • nombre: Nombre del catador

sesion_participante

Relación de catadores participantes en una sesión.

  • id: UUID (PK)
  • sesion_id: FK a sesion
  • catador_id: FK a auth.users
  • rol: Rol del participante (catador, juez, etc.)

muestra

Muestras de café evaluadas en una sesión.

  • id: UUID (PK)
  • sesion_id: FK a sesion
  • codigo: Código de la muestra (ej. "Muestra A-101")
  • posicion: Orden físico en la mesa

evaluacion

Evaluación completa de una muestra por un catador.

Campos de intensidades (JSONB):

  • intensidades: JSON con 8 parámetros (descriptiva 1-15, afectiva 1-10):
    • fragancia, aroma, sabor, saborResidual
    • acidez, dulzor, sensacionBoca, impresionGlobal

Campos de notas (JSONB arrays):

  • fragancia_aroma_notas: Array de objetos {categoria, subcategoria, notaEspecifica}
  • sabor_notas: Array de objetos {categoria, subcategoria, notaEspecifica}

Campos de arrays:

  • tazas_no_uniformes: smallint[] (valores 1-5)
  • tazas_defectuosas: smallint[] (valores 1-5)
  • sensacion_en_boca: text[] (Áspero, Suave, Aceitoso, Metálico, Astringente)
  • gustos_predominantes: text[] (1-2 elementos: Salado, Amargo, Ácido, Dulce, Umami)

Otros campos:

  • defecto: ENUM (Mohoso, Fenólico, Papa)
  • otras_notas: Texto libre
  • puntaje_final: int (calculado automáticamente por trigger)

Triggers Automáticos

  1. trg_eval_updated_at: Actualiza updated_at en cada UPDATE
  2. trg_eval_score_bi: Calcula puntaje_final como suma de valores afectivos

Constraints de Validación

  • Tazas no uniformes/defectuosas: solo valores 1-5
  • Sensaciones en boca: solo valores permitidos
  • Gustos predominantes: máximo 2 elementos
  • Intensidades descriptivas: rango 1-15
  • Intensidades afectivas: rango 1-10
  • Una evaluación única por participante/muestra

Funciones Auxiliares

get_promedio_parametro_afectivo(sesion_id, parametro)

Obtiene el promedio de un parámetro afectivo para toda una sesión.

SELECT get_promedio_parametro_afectivo(
  'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
  'acidez'
);

get_top_muestras(sesion_id, limit)

Obtiene las mejores muestras de una sesión ordenadas por puntaje.

SELECT * FROM get_top_muestras(
  'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
  3
);

Queries Típicas

Promedio de dulzor por sesión

SELECT s.id, AVG(((e.intensidades->'dulzor'->>'afectiva')::int))
FROM sesion s
JOIN muestra m ON m.sesion_id = s.id
JOIN evaluacion e ON e.muestra_id = m.id
WHERE s.id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
GROUP BY s.id;

Buscar evaluaciones con defecto específico

SELECT e.*
FROM evaluacion e
WHERE e.defecto = 'Fenólico';

Buscar taza defectuosa específica (usando índice GIN)

SELECT e.*
FROM evaluacion e
WHERE e.tazas_defectuosas @> ARRAY[5]::smallint[];

Top 3 muestras por puntaje

SELECT m.codigo, e.puntaje_final, sp.catador_id
FROM muestra m
JOIN evaluacion e ON e.muestra_id = m.id
JOIN sesion_participante sp ON sp.id = e.sesion_participante_id
WHERE m.sesion_id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
ORDER BY e.puntaje_final DESC
LIMIT 3;

Filtrar por acidez alta (usa índice funcional)

SELECT *
FROM evaluacion
WHERE ((intensidades->'acidez'->>'afectiva')::int) >= 8;

Datos de Prueba

El sistema viene con datos de prueba precargados:

  • 3 usuarios: Dario, Juan Pérez, María González
  • 1 sesión: "Mesa Laboratorio #1 - Lotes Lavados"
  • 3 muestras: A-101, B-202, C-303
  • 5 evaluaciones: Con variedad de puntajes, defectos, y características

Configuración

Credenciales por defecto (docker-compose.yml)

  • Base de datos: riocata
  • Usuario: riocata_user
  • Contraseña: riocata_password
  • Puerto: 5432

Modificar credenciales

Edita docker-compose.yml y cambia las variables de entorno:

environment:
  POSTGRES_DB: tu_base_de_datos
  POSTGRES_USER: tu_usuario
  POSTGRES_PASSWORD: tu_contraseña

Comandos Útiles

Ver logs de PostgreSQL

docker-compose logs -f postgres

Reiniciar la base de datos (¡CUIDADO! Borra todos los datos)

docker-compose down -v
docker-compose up -d

Backup de la base de datos

docker-compose exec postgres pg_dump -U riocata_user riocata > backup.sql

Restaurar backup

docker-compose exec -T postgres psql -U riocata_user -d riocata < backup.sql

Ejecutar script SQL desde el host

docker-compose exec -T postgres psql -U riocata_user -d riocata < mi_script.sql

Optimizaciones

El esquema incluye múltiples índices para optimizar queries comunes:

  • Índices B-tree en claves foráneas y puntaje_final
  • Índices GIN en JSONB y arrays (para operadores @>, &&, etc.)
  • Índices funcionales para valores específicos en JSONB
  • Índices de texto para búsqueda full-text en español

Próximos Pasos

  1. Frontend: Desarrollar UI en Nuxt 4
  2. API: Crear endpoints REST/GraphQL
  3. Autenticación: Integrar con Supabase Auth
  4. Análisis: Dashboards y reportes estadísticos
  5. Export: Generar PDFs de sesiones de catación

Desarrollo

Para desarrollar sobre este esquema:

  1. Los scripts en postgres/init/ solo se ejecutan la primera vez
  2. Para aplicar cambios, usa migrations o reinicia el contenedor con -v
  3. Los tests en postgres/tests/ son re-ejecutables sin problemas

Soporte

Para reportar problemas o sugerencias, contacta al equipo de Nucleo Rio Frio.


Desarrollado por Nucleo Rio Frio con Claude Code

Description
No description provided
Readme 8.1 MiB
Languages
Vue 58%
TypeScript 30.2%
CSS 4.6%
Shell 3.3%
PLpgSQL 2.7%
Other 1.2%