Proyecto Ithaka 2.0
DOCUMENTACIÓN TÉCNICA DEL PROYECTO: Ithaka Backoffice
- Propósito del documento
Este documento está pensado como entrega formal y referencia completa para cualquier desarrollador, integrador o auditor externo que necesite comprender la lógica, los endpoints, la estructura de la base de datos y las decisiones de diseño del proyecto "Ithaka Backoffice".
Describe:
- Estructura general del repositorio y archivos clave.
- Flujo de autenticación y seguridad.
- Todos los endpoints expuestos por la API: rutas, métodos, parámetros, validaciones, roles, schemas de entrada y salida, ejemplos y errores esperados.
- Modelos de datos (tablas), columnas, relaciones, restricciones y comportamiento (soft delete, JSON fields, check constraints).
- Servicios auxiliares (auditoría, exportación CSV) y sus contratos.
- Consideraciones, riesgos y recomendaciones para mantenimiento y evolución.
Audiencia: desarrolladores backend, QA, DevOps, integradores.
- Estructura del proyecto
Raíz relevante: ithaka-backoffice/app
Carpetas principales:
app/api/v1/endpoints/: Routers por recurso (auth, usuarios, casos, apoyos, asignaciones, notas, roles, convocatorias, catalogos, auditoria, metricas)app/models/: Modelos SQLAlchemy que representan tablas (Usuario, Rol, Caso, Emprendedor, Convocatoria, CatalogoEstados, CatalogoApoyo, Apoyo, ApoyoSolicitado, Programa, Asignacion, Nota, Auditoria)app/schemas/: Pydantic schemas para validación de requests/responses (uno por recurso)app/db/: Configuración de base de datos (engine, SessionLocal) y dependenciaget_dbapp/core/: Configuración y seguridad (config.pyysecurity.py)app/services/: Servicios auxiliares reutilizables (auditoría, exportación)app/api/deps.py: Dependencias comunes (get_db, etc.) — usado por endpointstests/: Tests unitarios/integración
Archivo de configuración: app/core/config.py (usa .env por default). La conexión a DB se construye con variables POSTGRES_*.
SQL de estructura: ithaka_backoffice.sql en raíz del repositorio (útil para revisar el DDL real en PostgreSQL).
- Cómo levantar localmente (resumen operativo)
Requisitos previos:
- Python 3.10+ (ver
requirements.txt) - PostgreSQL si se quiere usar DB real (o SQLite para pruebas locales si se ajusta la URL)
Variables de entorno (ver app/core/config.py):
- POSTGRES_USER
- POSTGRES_PASSWORD
- POSTGRES_DB
- POSTGRES_SERVER (default localhost)
- POSTGRES_PORT (default 5432)
- SECRET_KEY (JWT secret)
- ALGORITHM (HS256 por default)
- ACCESS_TOKEN_EXPIRE_MINUTES (ej: 30)
- REFRESH_TOKEN_EXPIRE_DAYS (ej: 30)
Comandos básicos:
python -m venv .venv .venv\Scripts\activate pip install -r requirements.txt
uvicorn app.main:app --reload --port 8000
Nota: el proyecto no incluye migraciones automáticas (Alembic). El DDL está en ithaka_backoffice.sql y los modelos están en app/models/.
- Seguridad y autenticación Tipo: JWT (JSON Web Tokens) Mecanismo:
- Login con
POST /api/v1/auth/login(body:email,password). Respuesta: access_token y refresh_token. access_tokentiene expiración corta (ACCESS_TOKEN_EXPIRE_MINUTES, por defecto 30 minutos).refresh_tokentiene expiración mayor (REFRESH_TOKEN_EXPIRE_DAYS).- Para endpoints protegidos, se usa el header:
Authorization: Bearer {access_token}.
Funciones clave en app/core/security.py:
hash_password(password): bcrypt para hashear contraseña.verify_password(plain, hash): compara plain con hash.create_access_token(data, expires_delta): genera JWT conexpy firma conSECRET_KEY.create_refresh_token(data, expires_delta): token de refresh similar.decode_access_token(token)/decode_refresh_token(token): decodifican y validan.security:HTTPBearer()para integrarse con Swagger UI.get_current_user(credentials, db): dependency que extrae el usuario a partir del access token.require_role(allowed_roles): fábrica de dependencia que valida quecurrent_user.rol.nombre_rolesté enallowed_roles.
Notas importantes:
- El logout no invalida tokens por defecto:
POST /api/v1/auth/logoutestá implementado para logging, pero la invalidación real solo existe si se implementa blacklist/tabla de tokens. require_rolecompara exactamente el nombre del rol. Mantener consistencia en nombres (Admin,Coordinador,Tutor, etc.). Cambiar mayúsculas/minúsculas puede romper verificaciones.
- Base de datos: modelos, columnas y relaciones
Resumen general: la app usa SQLAlchemy ORM. El engine se crea en
app/db/database.pyconcreate_engine(settings.DATABASE_URL). Las sesiones se crean conSessionLocaly la dependenciaget_dbenapp/db/session.pycierra la sesión al terminar.
Para cada tabla se indica: columnas, propósito, relaciones, restricciones y notas.
5.1 usuario (app/models/usuario.py)
- Tabla:
usuario - Columnas:
id_usuarioINTEGER PRIMARY KEY AUTOINCREMENT/serialnombreVARCHAR(150) NOT NULLapellidoVARCHAR(150) NULLemailVARCHAR(150) NOT NULL UNIQUEpassword_hashTEXT NOT NULLactivoBOOLEAN NOT NULL DEFAULT TRUEid_rolINTEGER NOT NULL FK ->rol.id_rol
- Propósito: Almacenar cuentas de acceso del personal que administra o interactúa con la plataforma (credenciales, estado y rol).
- Relaciones:
rol: relationship haciaRol(many-to-one). Back-populates:usuariosenRol.- Puede relacionarse a
Asignacion,Nota,Auditoria(referencias en otros modelos y endpoints).
- Observaciones:
- Se usa
activopara soft-delete (no se borran físicamente los usuarios para preservar auditorías y relaciones). password_hashNUNCA se expone por schemas de respuesta.
- Se usa
5.2 rol (app/models/rol.py)
- Tabla:
rol - Columnas:
id_rolINTEGER PKnombre_rolVARCHAR(50) NOT NULL UNIQUE
- Relaciones:
usuarios(one-to-many)
- Observaciones:
- Los endpoints de
rolvalidan unicidad y no permiten eliminar roles con usuarios asociados.
- Los endpoints de
- Propósito: Definir niveles de permiso y agrupar permisos por tipo para asignarlos a
usuario.
5.3 emprendedor (app/models/emprendedor.py)
- Tabla:
emprendedor - Columnas:
id_emprendedor,nombre,apellido,email,telefono,documento_identidad,pais_residencia,ciudad_residencia,campus_ucu,relacion_ucu,facultad_ucu,canal_llegada,motivacion,fecha_registro. - Relaciones:
casos(un emprendedor puede tener muchos casos). - Observaciones: Los endpoints no eliminan emprendedores por integridad.
- Propósito: Guardar la información de las personas emprendedoras que postulan o participan en proyectos, para vincularla con sus
casos.
5.4 convocatoria (app/models/convocatoria.py)
- Tabla:
convocatoria - Columnas:
id_convocatoria,nombre,fecha_cierre. - Relaciones:
casosback_populates conCaso.convocatoria. - Observaciones: Eliminación física está deshabilitada por integridad (endpoints comentados).
- Propósito: Representar una convocatoria o llamada a postulación, para agrupar casos por periodo/edición.
5.5 catalogo_estados (app/models/catalogo_estados.py)
- Tabla:
catalogo_estados - Columnas:
id_estado,nombre_estado,tipo_caso. - Restricciones:
CheckConstraint(lower(tipo_caso) IN ('postulacion','proyecto')). - Observaciones: El modelo valida y normaliza
nombre_estadoytipo_casoa minúsculas con@validates. - Propósito: Mantener el catálogo de estados posibles para clasificar y controlar el flujo de los
casosegún sutipo_caso.
5.6 caso (app/models/caso.py)
- Tabla:
caso - Columnas:
id_casoINTEGER PKfecha_creacionDateTime DEFAULT now()nombre_casoVARCHAR(200) NOT NULLdescripcionTEXTdatos_chatbotJSON (JSON/JSONB según DB)id_emprendedorFK ->emprendedor.id_emprendedorNOT NULLid_convocatoriaFK ->convocatoria.id_convocatoriaNULLABLEid_estadoFK ->catalogo_estados.id_estadoNOT NULL
- Relaciones:
emprendedorbackrefcasosconvocatoriaback_populatescasosestadobackrefcasosasignacionesrelationship conAsignacion(back_populates)
- Observaciones:
datos_chatbotpuede contener estructura libre JSON. En PostgreSQL conviene JSONB y consultas del tipodatos_chatbot->>'campo'.- Muchos endpoints devuelven una vista serializada del caso con campos calculados (nombre_estado, tipo_caso, tutor_nombre, etc.).
- Propósito: Entidad principal que representa una postulación o proyecto de un emprendedor; centraliza estado, datos del chatbot, convocatoria y relaciones con asignaciones, apoyos y notas.
5.7 catalogo_apoyo (app/models/catalogo_apoyo.py)
- Tabla:
catalogo_apoyo - Columnas:
id_catalogo_apoyo,nombreUNIQUE NOT NULL,descripcion,activoBOOLEAN. - Propósito: Tipos/categorías de apoyo.
5.8 apoyo (app/models/apoyo.py)
- Tabla:
apoyo - Columnas:
id_apoyo,id_catalogo_apoyoFK,fecha_inicio,fecha_fin,id_casoFK NOT NULL,id_programaFK NOT NULL. - Observaciones: Representa apoyos efectivamente otorgados a un caso.
- Propósito: Registrar intervenciones/beneficios concretos asignados a un caso (fechas, programa y tipo de apoyo), para seguimiento y reporting.
5.9 apoyo_solicitado (app/models/apoyo_solicitado.py)
- Tabla:
apoyo_solicitado - Columnas:
id_apoyo_solicitado,id_catalogo_apoyoFK NOT NULL,id_casoFK NOT NULL. - Propósito: Indica qué tipos de apoyo solicitó el caso (lista de preferencias/solicitudes).
5.10 programa (app/models/programa.py)
- Tabla:
programa - Columnas:
id_programa,nombre,activo. - Relaciones:
apoyos(backref desdeApoyo). - Propósito: Modelar las líneas o programas institucionales que financian o administran apoyos a los casos.
5.11 asignacion (app/models/asignacion.py)
- Tabla:
asignacion - Columnas:
id_asignacion,fecha_asignacionTIMESTAMP DEFAULT now(),id_usuarioFK NOT NULL,id_casoFK NOT NULL. - Relaciones:
usuario(backrefasignaciones),casoback_populatesasignaciones. - Observaciones: Un tutor puede estar asignado a múltiples casos; una asignación es única por par (id_usuario, id_caso) validada en lógica del endpoint.
- Propósito: Registrar qué usuario (tutor) está responsable de un caso en un momento dado, para control de responsabilidades y notificaciones.
5.12 nota (app/models/nota.py)
- Tabla:
nota - Columnas:
id_nota,contenido,tipo_nota,fechaTIMESTAMP DEFAULT now(),id_usuarioFK NOT NULL,id_casoFK NOT NULL. - Propósito: Registro de anotaciones o seguimientos realizados por staff.
5.13 auditoria (app/models/auditoria.py)
- Tabla:
auditoria - Columnas:
id_auditoria,timestamp,accion,valor_anterior,valor_nuevo,id_usuarioFK NOT NULL,id_casoFK NULLABLE. - Observaciones: Registro de trazabilidad de acciones. Los helpers en
app/services/auditoria_service.pyserializan valores (JSON/text) y añaden el registro a la sesión, pero NO hacen commit. - Propósito: Mantener un historial inmutable de cambios y acciones relevantes del sistema para auditoría, investigación y trazabilidad.
- Endpoints: descripción detallada por recurso
NOTA: todos los endpoints están bajo el prefijo base /api/v1 (convención del proyecto). A continuación se listan rutas, métodos, roles y comportamiento.
6.1 AUTH (app/api/v1/endpoints/auth.py)
-
POST /api/v1/auth/login
- Body:
LoginRequest({"email":EmailStr, "password":str}). - Respuesta:
LoginResponse({access_token, refresh_token, token_type, ACCESS_TOKEN_EXPIRE_MINUTES, usuario}). - Lógica: valida credenciales, verifica
usuario.activo, creaaccess_tokenyrefresh_tokencon payload{ sub, email, rol }. - Errores: 401 si email no existe o password incorrecto; 403 si usuario inactivo.
- Body:
-
GET /api/v1/auth/me
- Requiere Authorization Bearer token.
- Respuesta:
UsuarioActual(id_usuario, nombre, email, rol). - Lógica:
get_current_userdecodifica token y recupera usuario de la DB.
-
POST /api/v1/auth/logout
- Requiere Authorization.
- No invalida tokens por defecto. Se puede usar para auditoría o agregar blacklist si se implementa.
- Respuesta: message "Logout exitoso".
-
POST /api/v1/auth/refresh
- Body:
RefreshRequest({refresh_token}). - Respuesta:
RefreshResponse({access_token, token_type}). - Lógica: decodifica refresh token, valida usuario activo y genera nuevo access token.
- Errores: 401 si refresh token inválido o usuario inactivo.
- Body:
6.2 USUARIOS (app/api/v1/endpoints/usuario.py)
-
GET /api/v1/usuarios/
- Roles: Admin, Coordinador
- Query params:
skip,limit. - Respuesta: List[
UsuarioResponse].
-
GET /api/v1/usuarios/{usuario_id}
- Roles: Admin, Coordinador, Tutor (pero Tutor solo su propio perfil).
- Errores: 403 si Tutor intenta ver otro usuario; 404 si usuario no existe.
-
POST /api/v1/usuarios/
- Roles: Admin
- Body:
UsuarioCreate(incluyepassword). - Lógica: valida rol existente, valida email único, hashea password con
hash_password, creaUsuario, registra auditoría (registrar_auditoria_general) y commit. - Errores: 400 si rol inválido o email registrado.
-
PUT /api/v1/usuarios/{usuario_id}
- Roles: Admin, Coordinador, Tutor (pero no Admin puede cambiar su propio rol; Coordinador/Tutor solo su perfil)
- Body:
UsuarioUpdate(campos opcionales) - Lógica: validaciones de email único, solo Admin puede cambiar
id_roly no puede cambiar su propio rol.
-
DELETE /api/v1/usuarios/{usuario_id}
- Roles: Admin
- Lógica: soft-delete (set
activo=False). Protecciones: no puedes desactivar tu propio usuario. Auditoría registrada. - Respuesta: 204 No Content
-
PUT /api/v1/usuarios/{usuario_id}/reactivar
- Roles: Admin
- Lógica: vuelve a poner
activo=True.
6.3 ROLES (app/api/v1/endpoints/rol.py)
- GET /api/v1/roles/ (Admin)
- GET /api/v1/roles/{rol_id} (Admin)
- POST /api/v1/roles/ (Admin) Body:
RolCreate.- Valida unicidad
nombre_rol.
- Valida unicidad
- PUT /api/v1/roles/{rol_id} (Admin)
- Valida no duplicar nombre.
- DELETE /api/v1/roles/{rol_id} (Admin)
- Solo permitido si no hay usuarios con ese rol. En caso contrario retorna 400 con conteo.
6.4 PROGRAMAS (app/api/v1/endpoints/programa.py)
- GET /api/v1/programas/ (Admin, Coordinador, Tutor)
- GET /api/v1/programas/{programa_id}
- POST /api/v1/programas/ (Admin o Coordinador)
- PUT /api/v1/programas/{programa_id} (Admin o Coordinador)
- DELETE /api/v1/programas/{programa_id} (Admin)
- Validación: no permitir eliminar si hay
Apoyorelacionados (cuenta y retorna 400 si >0).
- Validación: no permitir eliminar si hay
6.5 CONVOCATORIAS (app/api/v1/endpoints/convocatoria.py)
- GET /api/v1/convocatorias/
- GET /api/v1/convocatorias/{id}
- POST /api/v1/convocatorias/ (Admin y Coordinador)
- Registra auditoría con
registrar_auditoria_general(nota: en código hay TODO temporal que usa id_usuario=1). Asegurarse de reemplazar porcurrent_user.id_usuariocuando se estabilice JWT en todos los endpoints.
- Registra auditoría con
- PUT /api/v1/convocatorias/{id} (Admin y Coordinador)
- DELETE: deshabilitado por integridad (comentado).
6.6 EMPRENDEDORES (app/api/v1/endpoints/emprendedores.py)
- GET /api/v1/emprendedores/
- Si
current_useres Tutor, se filtra para devolver solo emprendedores con casos asignados a ese tutor.
- Si
- GET /api/v1/emprendedores/{id}
- Tutor puede ver solo si tiene caso asignado al emprendedor.
- POST /api/v1/emprendedores/ (Admin)
- PUT /api/v1/emprendedores/{id} (Admin)
- GET /api/v1/emprendedores/{id}/casos -> devuelve
emprendedor.casos. - DELETE: deshabilitado (comentado) para preservar integridad referencial.
6.7 CASOS (app/api/v1/endpoints/caso.py)
-
GET /api/v1/casos/
- Filtros:
id_estado,tipo_caso,nombre_estado,id_emprendedor,id_convocatoria,id_tutor. - Si
current_useres Tutor, devuelve sólo casos asignados a ese tutor. - Devuelve una estructura normalizada con campos calculados:
nombre_estado,tipo_caso,emprendedor(nombre completo),convocatoria,tutor_nombre,id_tutor,asignacion.
- Filtros:
-
GET /api/v1/casos/export
- Exporta CSV (usa
ExportService). Parámetrocon_tutoresdetermina formato con tutores. - Respuesta: CSV en
ResponseconContent-Disposition. - Nota: exportar grandes volúmenes en memoria puede ser pesado; considerar streaming.
- Exporta CSV (usa
-
GET /api/v1/casos/{caso_id}
- Devuelve detalle del caso incluyendo programa de apoyo si existe, datos_chatbot, tutor, asignacion.
- Tutor solo si está asignado.
-
POST /api/v1/casos/
- Roles: Admin
- Body:
CasoCreate(no requiereid_estado; backend asigna estado 'postulado' buscando enCatalogoEstadospor nombre 'postulado' y tipo 'postulacion'). Si estado no existe, retorna 500 con mensaje. - Lógica: valida foreign keys (emprendedor, convocatoria) y maneja
IntegrityErrorcon mensajes legibles. - Registra auditoría con
registrar_auditoria_casoantes del commit.
-
PUT /api/v1/casos/{caso_id}
- Roles: Admin, Coordinador, Tutor (Tutor sólo si está asignado al caso)
- Body:
CasoUpdate(campos opcionales). Se usamodel_dump(exclude_unset=True)para aplicar solo cambios. - Registra auditoría con
valor_anterioryvalor_nuevosi hay cambios. - Manejo de
IntegrityErrorpara foreign keys inválidas.
6.8 APOYOS (app/api/v1/endpoints/apoyo.py)
-
GET /api/v1/apoyos/
- Filtros:
id_caso,id_programa. - Si Tutor, se filtra a apoyos de casos asignados.
- Filtros:
-
GET /api/v1/apoyos/{apoyo_id}
- Tutor solo si caso asignado.
-
GET /api/v1/apoyos/caso/{id_caso}
- Lista apoyos de un caso; verifica existencia del caso y autorización.
-
POST /api/v1/apoyos/
- Roles: Admin, Coordinador
- Body:
ApoyoCreate. - Lógica: valida existencia de
casoyprograma; creaApoyo, buscaAsignaciondel caso para determinarid_usuarioque registra auditoría. Siid_catalogo_apoyoes enviado, busca nombre enCatalogoApoyopara registrar texto legible. - Registra auditoría usando
registrar_auditoria_casoy commit.
-
PUT /api/v1/apoyos/{apoyo_id}` (Admin, Coordinador)
-
DELETE /api/v1/apoyos/{apoyo_id}` (Admin, Coordinador)
6.9 APOYOS SOLICITADOS (app/api/v1/endpoints/apoyo_solicitado.py)
- GET /api/v1/apoyo_solicitado/
- GET /api/v1/apoyo_solicitado/caso/{id_caso}
- GET /api/v1/apoyo_solicitado/{id}
- POST /api/v1/apoyo_solicitado/
- Roles: Admin, Coordinador
- Body:
ApoyoSolicitadoCreate(id_catalogo_apoyo, id_caso) - Verifica existencia de caso y catálogo.
- PUT /api/v1/apoyo_solicitado/{id}
- Solo permite cambiar
id_catalogo_apoyo.
- Solo permite cambiar
- DELETE /api/v1/apoyo_solicitado/{id}`
6.10 ASIGNACIONES (app/api/v1/endpoints/asignacion.py)
- GET /api/v1/asignaciones/
- Filtros:
id_caso,id_usuario.
- Filtros:
- GET /api/v1/asignaciones/{id}
- GET /api/v1/asignaciones/caso/{id_caso}
- GET /api/v1/asignaciones/usuario/{id_usuario}
- POST /api/v1/asignaciones/
- Roles: Admin, Coordinador, Tutor
- Body:
AsignacionCreate(id_usuario, id_caso) - Validaciones:
id_usuariodebe existir y su rol debe serTutor.- No permitir duplicados (mismo tutor en mismo caso) — ver consulta que valida existencia.
- Registrar auditoría con
registrar_auditoria_caso.
- PUT /api/v1/asignaciones/{id} : permite cambiar
id_usuario(validar que sea Tutor) - DELETE /api/v1/asignaciones/{id} : elimina asignación y registra auditoría.
6.11 NOTAS (app/api/v1/endpoints/nota.py)
- GET /api/v1/notas/
- Filtros:
id_caso,id_usuario. Tutor solo ve notas de casos asignados.
- Filtros:
- GET /api/v1/notas/{id}
- POST /api/v1/notas/
- Roles: Admin, Coordinador, Tutor (pero Tutor solo puede crear notas en casos asignados)
NotaCreaterequierecontenido,tipo_nota,id_usuario,id_caso.- Validaciones explícitas:
tipo_notaobligatorio y no vacío. - Se registra auditoría
nota_creadaconvalor_nuevoJSON.
- PUT /api/v1/notas/{id} : Tutor solo puede actualizar sus propias notas.
- DELETE /api/v1/notas/{id} : Tutor solo puede eliminar sus propias notas; auditoría registrada.
6.12 CATALOGOS (apoyos y estados)
-
catalogo_apoyos(app/api/v1/endpoints/catalogo_apoyos.py)- CRUD básico, Admin para crear/actualizar/eliminar; list y get disponibles para todos los roles.
- Maneja
ProgrammingErrorsi la tabla no existe (mensajes de error amigables). - Manejo de
IntegrityErrorpara unique constraint y retorno 409.
-
catalogo_estados(app/api/v1/endpoints/catalogo_estados.py)- CRUD (Admin para crear/actualizar/eliminar). Devuelve
nombre_estadoytipo_casoen minúscula para consistencia. tipo_casodebe serpostulacionoproyecto(CheckConstraint a nivel DB).
- CRUD (Admin para crear/actualizar/eliminar). Devuelve
6.13 AUDITORÍA (app/api/v1/endpoints/auditoria.py)
- GET /api/v1/auditoria/ (Admin, Coordinador)
- GET /api/v1/auditoria/staff/{id_usuario} (Admin, Coordinador, Tutor — coordinador/tutor solo su propio historial)
- Filtros:
id_caso,accion,skip,limit
- Filtros:
- GET /api/v1/auditoria/{id}` (Admin, Coordinador)
- Observación: Los registros de auditoría son de solo lectura vía API.
6.14 MÉTRICAS / DASHBOARD (app/api/v1/endpoints/metricas/dashboard.py)
- GET /api/v1/metricas/dashboard
- Parámetros:
id_convocatoriaopcional. - Retorna estructura compleja con:
totales(postulaciones, proyectos, proyectos incubados, tutores, emprendedores, apoyos)proyectos_por_estadoypostulaciones_por_estadoconcantidadyporcentaje.distribucion_apoyospor nombre delCatalogoApoyo.
- Implementación: consultas SQLAlchemy con
func.count,join,group_by, y uso decasepara ordenar estados por lista deseada (POSTULACION_ORDER y PROYECTO_ORDER).
- Parámetros:
- Servicios auxiliares
7.1 Auditoría (app/services/auditoria_service.py)
- Funciones:
_serializar_valor(valor): convierte estructuras a string/JSON para almacenar en la columnavalor_anterioryvalor_nuevo.registrar_auditoria_caso(db, accion, id_usuario, id_caso, valor_anterior, valor_nuevo): crea registro deAuditoriay lo añade a la sesión (NO hace commit).registrar_auditoria_general(db, accion, id_usuario, valor_anterior, valor_nuevo, id_caso=None): similar pero para auditorías no necesariamente ligadas a casos.
- Importante: estos helpers no ejecutan
db.commit(). Se espera que el llamador ejecute commit en la transacción principal. Esto evita inconsistencias (auditoría y operación en la misma transacción).
7.2 Exportación (app/services/export_service.py)
construir_query_casos(...): centraliza la construcción de queries reutilizables para listados y exportaciones (considera joins según filtros y rol delcurrent_user).exportar_casos_csv(...): genera CSV en memoria (StringIO) con campos del caso y datos del emprendedor.exportar_casos_con_tutores_csv(...): genera CSV con información de tutores y asignaciones.generar_nombre_archivo(tipo_reporte): retorna filename con timestamp.- Observación: Genera CSV en memoria; para conjuntos grandes, preferible stream o paginación para evitar consumo excesivo de memoria.
- Manejo de errores y patrones de transacción
- Uso de
db.flush()antes de commit en algunos endpoints para obtener ids generados (ej: crear usuario, crear caso). Sidb.flush()falla porIntegrityError, se hacedb.rollback()y se convierte el error a HTTP con mensaje legible. IntegrityErrores capturado en varios endpoints y mapeado a 400/409 según contexto (unique constraint -> 409).- Muchos endpoints llaman a funciones de auditoría antes del
db.commit().
- Tests
Carpeta tests/ contiene pruebas existentes: test_auditoria.py, test_auth.py, test_casos.py, test_export_casos.py, test_flujos_completos.py, test_permisos.py, test_usuarios.py.
Recomendación: ejecutar la suite tras configurar variables de entorno y una base de datos de test. Asegurarse de que la DB de test tenga el DDL aplicado (o usar fixtures que creen tablas en memoria).
- Riesgos, decisiones de diseño y recomendaciones (para futuro desarrollo y mantenimiento)
10.1 Migrations
- Actualmente no se observan migraciones automáticas. Recomiendo integrar Alembic para gestionar cambios en el schema y evitar divergencias entre modelos y la base de datos en producción.
10.2 Tokens y logout
- Implementar un mecanismo de revocación/blacklist de refresh/access tokens si se requiere invalidación inmediata en logout (ej: tabla
revoked_tokenso cache Redis con TTL).
10.3 Exportación de grandes volúmenes
ExportServiceconstruye y escribe en memoria. Para grandes datos usar streaming (StreamingResponse) o exportar por lotes y almacenar temporalmente en S3 o disco.
10.4 Transaccionalidad y auditoría
- Las funciones de auditoría no hacen commit: esto está correcto. Asegúrese de llamar a
registrar_auditoria_*dentro de la transacción y antes dedb.commit()para que auditoría y cambio sean atómicos.
10.5 Índices y performance
- Añadir índices en columnas que se filtran frecuentemente:
Usuario.emailya tiene index,Caso.id_emprendedor,Caso.id_estado,Apoyo.id_caso, etc. Revisar y añadir índices adicionales según métricas de uso.
10.6 CheckConstraints y normalización
catalogo_estadostiene CheckConstraint en minúsculas. En la capa de aplicación se normaliza, pero asegurar que inserciones directas a DB respeten la convención.
10.7 Manejo de datos JSON
datos_chatbotes JSON libre. Documentar el esquema esperado (key names y tipos) si el chatbot o el frontend consumen estructura específica.
10.8 Borrado lógico vs físico
Usuariousaactivo(soft delete). Otras tablas no usan soft delete. Consistencia: decidir estrategia (soft delete) para entidades críticas si se requiere mantener historial.
10.9 Nombres de rol y comparación
require_rolecompara texto exacto. Para evitar errores, crear constantes o enumeración centralizada de roles y utilizarla en creaciones y comprobaciones.
10.10 Manejo de errores DB
- Varios endpoints capturan
IntegrityErrory comparan contenido dee.origbuscando la palabra "foreign key" o "unique". Esto funciona pero depende del mensaje del driver DB. Mejor usar inspección más robusta (SQLAlchemye.orig.pgcodeen Postgres) o manejar con migraciones que definan constraints con nombres predecibles.
- Guía rápida para extender el proyecto
Añadir nuevo recurso (ejemplo: evento):
-
Crear modelo SQLAlchemy en
app/models/evento.pysiguiendoTEMPLATE.pyy añadir aapp/models/__init__.py. -
Crear schemas Pydantic en
app/schemas/evento.py(Create/Update/Response). -
Crear endpoints en
app/api/v1/endpoints/evento.pycon router y dependenciasget_db,require_rolesi necesario. -
Añadir pruebas en
tests/test_evento.py. -
Si el modelo modifica DB, crear migración Alembic (recomendado) o actualizar
ithaka_backoffice.sql. -
Documentos y recursos auxiliares en el repo
ithaka_backoffice.sql- DDL completo de la base de datos (examinar para conocer constraints, tipos y orden real de creación)README.md- Información general del proyectoapp/core/config.py- Variables de configuración yDATABASE_URLbuilderapp/core/security.py- Lógica JWT y validaciones (leer con atención para entender el flujo de autenticación)app/services/auditoria_service.py- Helpers para auditoríaapp/services/export_service.py- Lógica de exportación CSV y query builder reutilizable
- Conclusión
Este documento reúne la descripción funcional y técnica necesaria para que un tercero entienda cómo interactuar con la API, cómo funcionan las reglas de negocio principales, la organización de la base de datos y las decisiones clave tomadas en el diseño.