// IPSExtractor — esquema de integración PostgreSQL // Fuente: services/extraction/infrastructure/database.py // services/cleaning/infrastructure/database.py // // Visualizar en https://dbdiagram.io (importar este archivo) // o con la CLI de dbml: npx @dbml/cli dbml2sql docs/database.dbml Project ipsextractor { database_type: 'PostgreSQL' Note: ''' Base de datos compartida entre los microservicios de extracción y limpieza. Cada servicio define su propia copia del modelo SQLAlchemy y ejecuta `init_db()` con bloqueo advisory (pg_advisory_lock) para crear tablas sin condiciones de carrera. ''' } TableGroup pipeline { extraction_batches extraction_rows cleaning_jobs cleaned_rows } TableGroup async_jobs { tasks } // --------------------------------------------------------------------------- // Extracción // --------------------------------------------------------------------------- Table extraction_batches { id varchar(36) [pk, note: 'UUID v4'] status varchar(32) [not null, default: 'pending', note: 'pending | running | completed | failed'] root_path text [note: 'Ruta raíz de documentos procesados (IPS_DOCUMENT_ROOT)'] request jsonb [not null, default: '{}', note: 'ExtractionRequest serializado (keywords, fields, doc_types, etc.)'] error text created_at timestamptz [not null] updated_at timestamptz [not null] indexes { status } Note: 'Lote creado al iniciar una extracción; agrupa todas las filas extraídas de un procesamiento.' } Table extraction_rows { id integer [pk, increment] batch_id varchar(36) [not null, ref: > extraction_batches.id] radicado varchar(255) [note: 'Identificador del radicado IPS'] source_pdf text [note: 'Ruta del PDF origen dentro del volumen montado'] row_index integer [not null, default: 0, note: 'Orden estable dentro del lote'] row_json jsonb [not null, default: '{}', note: 'Fila aplanada con campos solicitados'] raw_json jsonb [not null, default: '{}', note: 'Resultado bruto del procesador por radicado'] created_at timestamptz [not null] indexes { batch_id radicado } Note: 'Una o más filas por radicado según flatten_extraction_result().' } // --------------------------------------------------------------------------- // Tareas asíncronas (API de extracción) // --------------------------------------------------------------------------- Table tasks { id varchar(36) [pk, note: 'UUID v4'] type varchar(64) [not null, note: 'p. ej. extraction'] status varchar(32) [not null, default: 'pending', note: 'pending | running | completed | failed'] title text request jsonb [not null, default: '{}', note: 'Payload de la petición que originó la tarea'] result jsonb [not null, default: '{}', note: 'Al completar incluye extraction_batch_id'] error text owner_id varchar(36) [note: 'Reservado para multi-usuario'] created_at timestamptz [not null] updated_at timestamptz [not null] started_at timestamptz finished_at timestamptz indexes { type status owner_id } Note: ''' Cola de trabajos en segundo plano del servicio de extracción. No hay FK a extraction_batches; el vínculo se guarda en result.extraction_batch_id al finalizar la tarea. ''' } // --------------------------------------------------------------------------- // Limpieza // --------------------------------------------------------------------------- Table cleaning_jobs { id varchar(36) [pk, note: 'UUID v4'] extraction_batch_id varchar(36) [not null, ref: > extraction_batches.id] status varchar(32) [not null, default: 'pending', note: 'pending | running | completed | failed'] request jsonb [not null, default: '{}', note: 'CleaningRequest (rules_per_column, cop_mod_*, etc.)'] log jsonb [not null, default: '[]', note: 'Lista de mensajes de auditoría del job'] error text created_at timestamptz [not null] updated_at timestamptz [not null] indexes { extraction_batch_id status } Note: 'Ejecución de reglas de limpieza sobre las filas de un lote de extracción.' } Table cleaned_rows { id integer [pk, increment] cleaning_job_id varchar(36) [not null, ref: > cleaning_jobs.id] extraction_batch_id varchar(36) [not null, ref: > extraction_batches.id] row_index integer [not null, default: 0, note: 'Conserva el orden del lote original'] row_json jsonb [not null, default: '{}', note: 'Fila tras aplicar reglas de limpieza'] created_at timestamptz [not null] indexes { cleaning_job_id extraction_batch_id } Note: 'Salida del servicio de limpieza; reemplaza las filas previas del mismo job en cada ejecución.' } // --------------------------------------------------------------------------- // Relaciones lógicas (sin FK en BD) // --------------------------------------------------------------------------- // tasks.result.extraction_batch_id -> extraction_batches.id (referencia en JSON)