feat: Add database migration service with Alembic for PostgreSQL, integrating job management and counting functionalities. Update Docker Compose to include db-migrations service, enhancing the microservices architecture. Revise documentation for clarity on database schema and migration processes.
This commit is contained in:
@@ -1,276 +1,114 @@
|
||||
## IPSExtractor Microservices
|
||||
# IPSExtractor
|
||||
|
||||
Monorepo con servicios separados como si fueran repos independientes. Cada
|
||||
servicio tiene su propia API, dependencias, Dockerfile, codigo de dominio e
|
||||
infraestructura local. No hay paquetes compartidos entre servicios.
|
||||
Sistema de extracción y análisis de datos desde facturas médicas PDF.
|
||||
Arquitectura de microservicios: cada servicio tiene su propia API, dependencias, Dockerfile y código de dominio. No hay paquetes compartidos entre servicios.
|
||||
|
||||
```text
|
||||
services/
|
||||
extraction/ Repo/servicio de extraccion
|
||||
api/ API FastAPI
|
||||
domain/ Dominio propio de extraccion y normalizacion
|
||||
core/ Utilidades propias para PDF, texto y filas
|
||||
infrastructure/ Persistencia propia del servicio
|
||||
requirements.txt
|
||||
Dockerfile
|
||||
---
|
||||
|
||||
cleaning/ Repo/servicio de limpieza
|
||||
api/ API FastAPI
|
||||
domain/ Reglas propias de limpieza
|
||||
infrastructure/ Persistencia propia del servicio
|
||||
requirements.txt
|
||||
Dockerfile
|
||||
## Servicios
|
||||
|
||||
api_gateway/ API central opcional
|
||||
api/ Proxy HTTP hacia extraction y cleaning
|
||||
requirements.txt
|
||||
Dockerfile
|
||||
| Servicio | Puerto | Tecnología | Descripción |
|
||||
|----------|--------|------------|-------------|
|
||||
| `api_gateway` | 8000 | Python/FastAPI | Puerta de enlace única; enruta hacia los demás servicios |
|
||||
| `extraction` | 8001 | Python/FastAPI | Extracción LLM de campos; orquestación de jobs y conteo |
|
||||
| `cleaning` | 8002 | Python/FastAPI | Normalización y limpieza de datos extraídos |
|
||||
| `count-api` | 8080 (→ 8081) | Go | Conteo de servicios en facturas con OCR + ONNX (sin estado) |
|
||||
| `database` | 5432 | PostgreSQL 16 | Base de datos compartida |
|
||||
| `redis` | 6379 | Redis 7 | Cola ARQ para workers de extracción |
|
||||
| `desktop_app` | local | Electron + React | Aplicación de escritorio |
|
||||
|
||||
desktop_app/ App desktop Windows/Mac
|
||||
electron/ Main process, preload y dialogos nativos
|
||||
src/ React UI, cliente API y export CSV
|
||||
package.json
|
||||
---
|
||||
|
||||
## Flujo principal
|
||||
|
||||
```
|
||||
Desktop app
|
||||
│
|
||||
├─ Conteo (OCR + ML)
|
||||
│ ├─ POST /count/v1/count → count-api (lotes de 10 PDFs)
|
||||
│ ├─ POST /count-jobs → extraction (guarda en PostgreSQL)
|
||||
│ └─ PATCH /count-jobs → extraction (validación humana)
|
||||
│
|
||||
└─ Extracción (LLM)
|
||||
├─ POST /tasks/extraction/from-count → extraction (lee desde /data)
|
||||
├─ POST /tasks/{id}/ingest-many → extraction (subida de PDFs)
|
||||
└─ GET /tasks/{id}/rows → extraction (resultados)
|
||||
```
|
||||
|
||||
La base de datos Postgres es compartida como contrato de integracion, pero el
|
||||
codigo de acceso a datos esta duplicado dentro de cada servicio para que puedan
|
||||
vivir por separado.
|
||||
---
|
||||
|
||||
### Puertos
|
||||
|
||||
| Servicio | Puerto | Uso |
|
||||
| --- | ---: | --- |
|
||||
| `api_gateway` | `8000` | API central opcional |
|
||||
| `extraction` | `8001` | API propia de extraccion |
|
||||
| `cleaning` | `8002` | API propia de limpieza |
|
||||
| `database` | `5432` | Postgres |
|
||||
| `desktop_app` | local | App desktop fuera de Docker |
|
||||
|
||||
Documentacion interactiva:
|
||||
|
||||
- `http://localhost:8000/docs`
|
||||
- `http://localhost:8001/docs`
|
||||
- `http://localhost:8002/docs`
|
||||
|
||||
### Variables
|
||||
|
||||
```env
|
||||
IPS_DOCUMENT_ROOT=C:/ruta/a/documentos
|
||||
|
||||
# Ejemplo: extraccion con Grok/xAI
|
||||
EXTRACTOR_PROVIDER=xai
|
||||
EXTRACTOR_BASE_URL=https://api.x.ai/v1
|
||||
EXTRACTOR_API_KEY=tu_xai_api_key
|
||||
EXTRACTOR_MODEL=grok-tu-modelo
|
||||
EXTRACTOR_TIMEOUT_S=120
|
||||
|
||||
# Ejemplo: normalizacion con OpenAI
|
||||
NORMALIZER_PROVIDER=openai
|
||||
NORMALIZER_BASE_URL=https://api.openai.com/v1
|
||||
NORMALIZER_API_KEY=tu_openai_api_key
|
||||
NORMALIZER_MODEL=gpt-tu-modelo
|
||||
NORMALIZER_TIMEOUT_S=120
|
||||
|
||||
PDF_DPI=600
|
||||
PDF_THREAD_COUNT=10
|
||||
CONCURRENCY_LIMIT=4
|
||||
ALLOWED_DOCUMENT_TYPES=CC,TI,CE,RC,PA,PPT,PEP,EPT,NUIP
|
||||
```
|
||||
|
||||
Si `NORMALIZER_BASE_URL` o `NORMALIZER_API_KEY` se dejan vacios, el servicio usa
|
||||
el mismo backend configurado en `EXTRACTOR_BASE_URL` y `EXTRACTOR_API_KEY`.
|
||||
|
||||
Dentro de Docker, `IPS_DOCUMENT_ROOT` se monta como `/data`.
|
||||
|
||||
### Levantar
|
||||
## Arrancar el stack completo
|
||||
|
||||
```bash
|
||||
# Clonar y configurar
|
||||
cp .env.example .env
|
||||
# Editar .env con las API keys del LLM y la ruta de documentos
|
||||
|
||||
docker compose up --build
|
||||
```
|
||||
|
||||
Health checks:
|
||||
|
||||
```bash
|
||||
curl http://localhost:8000/health
|
||||
curl http://localhost:8001/health
|
||||
curl http://localhost:8002/health
|
||||
curl http://localhost:8000/health # gateway (agrega estado de los 3 servicios)
|
||||
curl http://localhost:8081/health # count-api (directo)
|
||||
```
|
||||
|
||||
### Flujo Directo Por Servicio
|
||||
---
|
||||
|
||||
1. Ver modelos disponibles en extraccion:
|
||||
## Variables de entorno principales
|
||||
|
||||
```bash
|
||||
curl http://localhost:8001/models
|
||||
| Variable | Descripción |
|
||||
|----------|-------------|
|
||||
| `IPS_DOCUMENT_ROOT` | Carpeta local con los documentos PDF (montada en `/data`) |
|
||||
| `EXTRACTOR_API_KEY` | API key del LLM (OpenAI, xAI, etc.) |
|
||||
| `EXTRACTOR_BASE_URL` | URL base del LLM (default: OpenAI) |
|
||||
| `EXTRACTOR_MODEL` | Modelo de extracción (default: `gpt-5.4-mini`) |
|
||||
| `WORKER_REPLICAS` | Réplicas del worker de extracción (default: `2`) |
|
||||
|
||||
Ver `.env.example` para la lista completa.
|
||||
|
||||
---
|
||||
|
||||
## Estructura del repositorio
|
||||
|
||||
```
|
||||
services/
|
||||
api_gateway/ — proxy HTTP (Python) → README
|
||||
extraction/ — LLM + jobs + conteo BD (Python) → README
|
||||
cleaning/ — limpieza de datos (Python) → README
|
||||
count-api/ — OCR + ONNX stateless (Go) → README
|
||||
db/ — migraciones Alembic → README
|
||||
desktop_app/ — app Electron + React → README
|
||||
docs/
|
||||
database.dbml — esquema completo de la BD
|
||||
docker-compose.yml — stack completo
|
||||
.env.example — variables de entorno de referencia
|
||||
```
|
||||
|
||||
2. Descubrir campos:
|
||||
---
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8001/schemas \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"document_path": "/data/radicado-1/factura.pdf"}'
|
||||
## Base de datos (esquema resumido)
|
||||
|
||||
```
|
||||
jobs — contenedor de un trabajo del usuario
|
||||
tasks — módulo ejecutado (counting / extraction / cleaning)
|
||||
count_job_files — resultado de conteo por PDF
|
||||
extraction_batches — lote de extracción
|
||||
extraction_rows — fila extraída por documento
|
||||
cleaning_jobs — job de limpieza
|
||||
cleaned_rows — fila transformada
|
||||
```
|
||||
|
||||
3. Ejecutar extraccion:
|
||||
Esquema completo: [`docs/database.dbml`](docs/database.dbml)
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8001/batches \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"root_path": "/data",
|
||||
"keywords": ["factura"],
|
||||
"fields": ["numero_factura", "items/*/codigo", "items/*/valor"],
|
||||
"alias_map": {
|
||||
"numero_factura": "factura",
|
||||
"items/*/codigo": "codigo",
|
||||
"items/*/valor": "valor"
|
||||
},
|
||||
"structure_mode": "radicados"
|
||||
}'
|
||||
```
|
||||
---
|
||||
|
||||
La respuesta devuelve `id`; ese valor es el `batch_id`.
|
||||
## Documentación por servicio
|
||||
|
||||
4. Consultar filas extraidas:
|
||||
|
||||
```bash
|
||||
curl http://localhost:8001/batches/{batch_id}/rows
|
||||
```
|
||||
|
||||
5. Ver reglas de limpieza:
|
||||
|
||||
```bash
|
||||
curl http://localhost:8002/rules
|
||||
```
|
||||
|
||||
6. Analizar columnas extraidas:
|
||||
|
||||
```bash
|
||||
curl http://localhost:8002/batches/{batch_id}/analysis
|
||||
```
|
||||
|
||||
7. Ejecutar limpieza:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8002/jobs \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"extraction_batch_id": "{batch_id}",
|
||||
"rules_per_column": {
|
||||
"valor": ["to_integer"],
|
||||
"fecha": ["date_to_ddmmyyyy"]
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
La respuesta devuelve `id`; ese valor es el `job_id`.
|
||||
|
||||
8. Consultar filas limpias:
|
||||
|
||||
```bash
|
||||
curl http://localhost:8002/jobs/{job_id}/rows
|
||||
```
|
||||
|
||||
### Flujo Por API Gateway
|
||||
|
||||
El gateway no importa codigo interno de los servicios; solo enruta HTTP.
|
||||
|
||||
```bash
|
||||
curl http://localhost:8000/models
|
||||
curl http://localhost:8000/config
|
||||
curl http://localhost:8000/extraction/batches/{batch_id}/rows
|
||||
curl http://localhost:8000/cleaning/jobs/{job_id}/rows
|
||||
```
|
||||
|
||||
Para llamar cualquier endpoint propio de un servicio:
|
||||
|
||||
- `http://localhost:8000/extraction/...`
|
||||
- `http://localhost:8000/cleaning/...`
|
||||
|
||||
### Desarrollo Independiente
|
||||
|
||||
Extraccion como repo aparte:
|
||||
|
||||
```bash
|
||||
cd services/extraction
|
||||
pip install -r requirements.txt
|
||||
uvicorn api.main:app --host 0.0.0.0 --port 8001
|
||||
```
|
||||
|
||||
Limpieza como repo aparte:
|
||||
|
||||
```bash
|
||||
cd services/cleaning
|
||||
pip install -r requirements.txt
|
||||
uvicorn api.main:app --host 0.0.0.0 --port 8002
|
||||
```
|
||||
|
||||
Gateway como repo aparte:
|
||||
|
||||
```bash
|
||||
cd services/api_gateway
|
||||
pip install -r requirements.txt
|
||||
uvicorn api.main:app --host 0.0.0.0 --port 8000
|
||||
```
|
||||
|
||||
App desktop como repo aparte:
|
||||
|
||||
```bash
|
||||
cd services/desktop_app
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
La app desktop usa `http://localhost:8000` por defecto. **No monta la carpeta
|
||||
en Docker**: indexa los documentos localmente y los sube uno a uno al API
|
||||
(`POST /extraction/schemas/upload` y `POST /tasks/{id}/ingest`). Solo se
|
||||
persisten las filas extraidas en Postgres; los PDFs permanecen en el equipo del
|
||||
usuario.
|
||||
|
||||
```bash
|
||||
cd services/desktop_app
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
#### Flujo desktop (sin volumen `/data`)
|
||||
|
||||
1. Seleccionar carpeta local en la app.
|
||||
2. Descubrir campos (sube un PDF de muestra por tipo).
|
||||
3. Ejecutar extraccion (crea tarea `source=desktop` e ingesta cada archivo).
|
||||
4. Consultar tareas y exportar CSV opcional.
|
||||
|
||||
#### Modo legacy API / curl
|
||||
|
||||
Si llamas al API con `root_path` y rutas bajo `/data`, monta documentos al
|
||||
levantar Docker:
|
||||
|
||||
```bash
|
||||
IPS_DOCUMENT_ROOT=C:/ruta/a/documentos docker compose up --build
|
||||
```
|
||||
|
||||
### Error: No se encontraron documentos en /data
|
||||
|
||||
Ese mensaje aparece en clientes legacy que envian `root_path=/data` sin montar
|
||||
documentos. La app desktop no deberia usar ese modo. Verifica:
|
||||
|
||||
```bash
|
||||
docker exec ipsextractor-extraction ls -la /data
|
||||
```
|
||||
|
||||
Para la app desktop, asegurate de usar Electron (`npm run dev`) y de que el
|
||||
gateway responda en `http://localhost:8000`.
|
||||
|
||||
### Contrato De Base De Datos
|
||||
|
||||
Los servicios se integran por Postgres:
|
||||
|
||||
- `extraction_batches`
|
||||
- `extraction_rows`
|
||||
- `tasks`
|
||||
- `cleaning_jobs`
|
||||
- `cleaned_rows`
|
||||
|
||||
Cada servicio mantiene su propia copia del modelo SQLAlchemy para no depender
|
||||
de un paquete comun.
|
||||
- [`services/api_gateway/README.md`](services/api_gateway/README.md)
|
||||
- [`services/extraction/README.md`](services/extraction/README.md)
|
||||
- [`services/cleaning/README.md`](services/cleaning/README.md)
|
||||
- [`services/services-count-facturas-medicas-serving/README.md`](services/services-count-facturas-medicas-serving/README.md)
|
||||
- [`services/db/README.md`](services/db/README.md)
|
||||
- [`services/desktop_app/README.md`](services/desktop_app/README.md)
|
||||
|
||||
Reference in New Issue
Block a user