feat: Update .env.example and Docker Compose configurations to enhance service performance and health checks. Adjust OCR settings for improved document processing efficiency, and refine API Gateway health checks for better service monitoring. Revise desktop application documentation to reflect new API Gateway URL and installation instructions.

This commit is contained in:
erik
2026-07-06 04:30:27 -05:00
parent 081da999d4
commit c37c40663d
39 changed files with 4815 additions and 1232 deletions
+92 -14
View File
@@ -138,6 +138,8 @@ class ExtractionRequest(BaseModel):
radicado_filter: list[str] | None = None
documents: list[DesktopDocument] | None = None
document_count: int | None = None
job_id: str | None = None # vincular al job existente
model: str | None = None
@model_validator(mode="after")
def validate_source(self) -> "ExtractionRequest":
@@ -538,6 +540,7 @@ def create_extraction_task(request: ExtractionRequest, background_tasks: Backgro
session.flush()
batch_id = batch.id
task = Task(
job_id=request.job_id,
type="extraction",
status="running",
title=title,
@@ -560,6 +563,7 @@ def create_extraction_task(request: ExtractionRequest, background_tasks: Backgro
with session_scope() as session:
task = Task(
job_id=request.job_id,
type="extraction",
status="pending",
title=f"Extraccion {request.root_path}",
@@ -966,6 +970,52 @@ def serialize_job(job: Job) -> dict[str, Any]:
}
class CreateJobRequest(BaseModel):
title: str | None = None
folder_path: str | None = None
class UpdateJobRequest(BaseModel):
title: str | None = None
folder_path: str | None = None
status: str | None = None
@app.post("/jobs", response_model=JobRead)
def create_job(request: CreateJobRequest) -> dict[str, Any]:
"""Crea un trabajo (job) independiente como contenedor para módulos."""
with session_scope() as session:
job = Job(
status="pending",
title=request.title,
job_metadata={"folder_path": request.folder_path} if request.folder_path else {},
updated_at=utcnow(),
)
session.add(job)
session.flush()
return serialize_job(job)
@app.patch("/jobs/{job_id}", response_model=JobRead)
def update_job(job_id: str, request: UpdateJobRequest) -> dict[str, Any]:
"""Actualiza título, metadata o estado de un job."""
with session_scope() as session:
job = session.get(Job, job_id)
if job is None:
raise HTTPException(status_code=404, detail="Job no encontrado")
if request.title is not None:
job.title = request.title
if request.status is not None:
job.status = request.status
if request.folder_path is not None:
meta = dict(job.job_metadata or {})
meta["folder_path"] = request.folder_path
job.job_metadata = meta
job.updated_at = utcnow()
session.flush()
return serialize_job(job)
@app.get("/jobs", response_model=list[JobRead])
def list_jobs() -> list[dict[str, Any]]:
with session_scope() as session:
@@ -1011,6 +1061,7 @@ class CountJobFileCreate(BaseModel):
class CreateCountJobRequest(BaseModel):
title: str | None = None
job_id: str | None = None # si se pasa, se usa el job existente
results: list[CountJobFileCreate]
@@ -1036,6 +1087,8 @@ class UpdateCountJobFileRequest(BaseModel):
class ExtractionFromCountRequest(BaseModel):
counting_task_id: str
min_service_count: int = 0
valid_counts: list[int] | None = None # conteos exactos válidos (vacío = todos)
job_id: str | None = None # vincular al job existente del conteo
fields: list[str] = Field(default_factory=list)
alias_map: dict[str, str] = Field(default_factory=dict)
doc_types: list[DocumentType] | None = None
@@ -1067,20 +1120,30 @@ def serialize_count_file(f: CountJobFile) -> dict[str, Any]:
def create_count_job(request: CreateCountJobRequest) -> dict[str, Any]:
"""Persiste los resultados del servicio de conteo de facturas en la BD compartida."""
total = len(request.results)
default_title = request.title or f"Conteo ({total} archivos)"
with session_scope() as session:
job = Job(
status="completed",
title=request.title or f"Conteo ({total} archivos)",
updated_at=utcnow(),
)
session.add(job)
if request.job_id:
job = session.get(Job, request.job_id)
if job is None:
raise HTTPException(status_code=404, detail="Job no encontrado")
if request.title:
job.title = request.title
job.status = "running"
job.updated_at = utcnow()
else:
job = Job(
status="completed",
title=default_title,
updated_at=utcnow(),
)
session.add(job)
session.flush()
task = Task(
job_id=job.id,
type="counting",
status="completed",
title=job.title,
title=default_title,
request={},
result={
"total": total,
@@ -1266,11 +1329,19 @@ def create_extraction_from_count(
)
).all()
use_exact_counts = request.valid_counts is not None and len(request.valid_counts) > 0
valid_files = [
f for f in all_files
if f.status == "ok"
and f.is_valid is not False
and (f.service_count is None or f.service_count >= request.min_service_count)
and (
# Si el usuario marcó manualmente como válido, siempre se incluye
f.is_valid is True
# Si hay conteos exactos definidos, el service_count debe estar en la lista
or (use_exact_counts and f.service_count in request.valid_counts)
# Sin filtro exacto, usar umbral mínimo (compatibilidad hacia atrás)
or (not use_exact_counts and (f.service_count is None or f.service_count >= request.min_service_count))
)
]
if not valid_files:
@@ -1292,12 +1363,19 @@ def create_extraction_from_count(
]
total = len(files_data)
job = Job(
status="running",
title=f"Extraccion desde conteo ({total} archivos)",
updated_at=utcnow(),
)
session.add(job)
if request.job_id:
job = session.get(Job, request.job_id)
if job is None:
raise HTTPException(status_code=404, detail="Job no encontrado")
job.status = "running"
job.updated_at = utcnow()
else:
job = Job(
status="running",
title=f"Extraccion desde conteo ({total} archivos)",
updated_at=utcnow(),
)
session.add(job)
session.flush()
batch = ExtractionBatch(
@@ -27,7 +27,13 @@ DATABASE_URL = os.getenv(
"postgresql+psycopg://ipsextractor:ipsextractor@database:5432/ipsextractor",
)
engine = create_engine(DATABASE_URL, pool_pre_ping=True)
engine = create_engine(
DATABASE_URL,
pool_pre_ping=True,
pool_size=10,
pool_timeout=30,
connect_args={"connect_timeout": 10},
)
SessionLocal = sessionmaker(bind=engine, autoflush=False, autocommit=False)