README

← Volver a documentación

SECOP Analize

Visor y analizador local de los procesos de contratación pública de Colombia (SECOP I y SECOP II), con análisis IA on-demand de los pliegos para evaluar requisitos de aplicación.

Qué hace

  • Importa ~3 M procesos de la SODA API (datos.gov.co) a SQLite local — incremental diario sin captcha.
  • Visualiza procesos con búsqueda, filtros y vista de detalle.
  • Analiza con IA los anexos (PDF/DOCX/XLSX) de procesos puntuales: descarga, extrae texto (con OCR si necesario) y genera un reporte ejecutivo con Ollama indicando qué empresa puede aplicar, requisitos de experiencia, capacidad financiera, banderas rojas, etc.

Arquitectura

┌────────────────────────────────────────────────────────────┐
│  Navegador en localhost:4567                               │
└──────────────────────┬─────────────────────────────────────┘
                       │
┌──────────────────────▼─────────────────────────────────────┐
│  Sinatra (Ruby) en 127.0.0.1:4567                          │
│    • bin/web — dashboard + detalle + API de relay          │
└──────────┬──────────────────────────┬──────────────────────┘
           │                          │ HTTP relay
┌──────────▼─────────────────┐   ┌────▼──────────────────────┐
│  SQLite WAL                │   │  Node service en 5678     │
│  db/secop.sqlite3 (~13 GB) │   │    • bin/anexos-service   │
│                            │   │    • Chromium persistente │
│  Tablas:                   │   │    • Sesión SECOP II      │
│   procesos (2.99 M)        │   │      reusable             │
│   proceso_anexos           │◀──┤    • Ollama llm           │
│   import_state             │   │      qwen2.5:14b          │
│   scrape_state             │   └───────────────────────────┘
└────────────────────────────┘
           ▲
           │ daily 03:00
┌──────────┴─────────────────┐
│  launchd                   │
│  com.secop.import-daily    │
│   → bin/import-daily.sh    │
│   → bin/import-api.mjs     │
│   → datos.gov.co (SODA)    │
└────────────────────────────┘

URLs

ServicioURLDescripción
Dashboardhttp://127.0.0.1:4567/Listado, búsqueda, KPIs
Detalle procesohttp://127.0.0.1:4567/proceso/:numero_constanciaVista con análisis IA
Filtro solo IAhttp://127.0.0.1:4567/?con_resumen=1Procesos con análisis
Servicio Node (interno)http://127.0.0.1:5678/healthStatus del browser + sesión
Ollamahttp://localhost:11434LLM local (qwen2.5:14b)

Stack

ComponenteVersiónPara qué
Ruby (rbenv)3.4.7Sinatra dashboard + Sequel ORM
Node.js (nvm)v22.21.1Pipeline anexos + servicio persistente
SQLite3.53BD local con WAL
Playwright1.59Chromium headless para enumerar anexos
Ollama0.23+LLM local
qwen2.5:14b9 GBModelo IA para análisis
Tesseract5.5OCR español para PDFs escaneados
Poppler26pdftotext, pdftoppm
Antiword, unrtfDOC legacy / RTF

Instalación

# Dependencias del sistema (Homebrew)
brew install ollama poppler tesseract antiword unrtf

# Modelo LLM
brew services start ollama
ollama pull qwen2.5:14b

# Dependencias Node
cd "/Users/mac/RubymineProjects/SECOP Analize"
npm install
npx playwright install chromium

# Dependencias Ruby
bundle install

Arrancar los servicios

Una sola línea — bin/start.sh mata instancias previas y lanza Sinatra (:4567) + servicio Node (:5678) en background:

cd "/Users/mac/RubymineProjects/SECOP Analize" && bin/start.sh

Verifica Ollama corriendo (necesario para análisis IA):

brew services start ollama

Abre http://127.0.0.1:4567/.

Para detener todo:

pkill -f 'anexos-service\|bin/web'

Para arrancar solo uno:

bin/start.sh web      # solo Sinatra
bin/start.sh service  # solo servicio Node

Logs en /tmp/secop-web.log y /tmp/secop-service.log.

Windows (cross-platform)

El stack corre igual en Windows y macOS desde el mismo repo. En Windows se usa Ruby (RubyInstaller+DevKit), Node y PowerShell; los scripts .sh/launchd de macOS tienen equivalentes .ps1 y .mjs.

# Dependencias Ruby y Node (una vez)
bundle install
npm install                       # baja el binario nativo de better-sqlite3 para Windows
npx playwright install chromium

# (opcional, para IA) instala Ollama para Windows desde https://ollama.com/download
#   ollama pull qwen2.5:14b

# Arrancar
pwsh bin/start.ps1                 # web (:4567) + servicio Node (:5678)
pwsh bin/start.ps1 web            # solo Sinatra
pwsh bin/stop.ps1                 # detener

# Import diario programado (equivalente del launchd plist)
pwsh bin/register-daily-task.ps1  # registra la tarea a las 03:00
Start-ScheduledTask -TaskName "SECOP import diario"   # disparar ahora

Notas:

  • Puma corre en single-mode en Windows (workers 0): Windows no tiene fork(), así que el modo cluster no aplica. config/puma.rb lo detecta automáticamente.
  • Logs en log\secop-web.log, log\secop-service.log y log\import\YYYY-MM-DD.log (en macOS siguen yendo a /tmp y ~/Library/Logs).
  • Para scripts Ruby bajo bundler usar ruby -rbundler/setup script.rb (en Windows bundle exec ruby tiene un bug con el flag -2); los .ps1 ya lo hacen.
  • Equivalentes cross-platform: rebuild-competidores.shrebuild-competidores.mjs, backup.shbackup.mjs (ambos usan better-sqlite3 en vez del CLI sqlite3, ausente en Windows).
  • Dependencias de IA/OCR externas (Ollama, Poppler/pdftotext, Tesseract) deben instalarse aparte en Windows; sin ellas, el import diario corre igual pero salta los pasos de IA/OCR (no fatal).

Cómo usar

Buscar / filtrar procesos

  • Búsqueda libre (top bar): por número de proceso, entidad, objeto o detalle. Case-insensitive.
  • Chips rápidos: Solo IA, Adjudicados, SECOP I, SECOP II.
  • Filtros avanzados (colapsable): tipo, estado, departamento, entidad LIKE, cuantía rango, fecha rango.
  • Sort: clic en headers de tabla.

Analizar un proceso con IA

  1. Entra al detalle de un proceso (/proceso/CO1.REQ.…).
  2. Si la sesión SECOP II está expirada, clic en "Renovar sesión SECOP II" — se abre Chrome, pasa el captcha, navega a cualquier proceso para ver "Documentos", cierra Chrome.
  3. Clic en "Procesar con IA". Toma ~5-25 min según cantidad de anexos.
  4. Cuando termina, la página se recarga mostrando:
  • Veredicto con color (verde apto / amarillo cautela / rojo amarrado)
  • Cifras clave, Tipo de empresa, Experiencia exigida, Capacidad financiera
  • Equipo profesional (tabla), Documentos, Cronograma
  • Análisis crítico + Recomendación final
  1. Puedes Imprimir / Guardar PDF del reporte.

Import diario automático (launchd)

Configuración ya instalada en ~/Library/LaunchAgents/com.secop.import-daily.plist. Corre cada día a las 03:00 AM:

# Ver estado
launchctl list | grep secop

# Disparar manualmente (sin esperar a las 3 AM)
launchctl kickstart "gui/$(id -u)/com.secop.import-daily"

# Ver último log
tail -50 ~/Library/Logs/secop-import/$(date +%Y-%m-%d).log

# Desactivar
launchctl unload ~/Library/LaunchAgents/com.secop.import-daily.plist

El import es incremental — solo trae procesos publicados después del último watermark (en la tabla import_state). Sin captcha porque usa la API SODA oficial.

Scripts en bin/

ScriptPropósito
webInicia Sinatra (Ruby)
anexos-service.mjsServicio Node persistente — Chromium + Ollama, endpoint HTTP
secop2-login.mjsAbre Chrome headed para pasar reCAPTCHA y guardar sesión
import-api.mjsImport desde SODA (INCREMENTAL=1 para solo nuevos)
import-daily.shWrapper que detecta node correcto + maneja logs (lanzado por launchd)
anexos.mjsPipeline batch CLI (alternativa al servicio)
anexos-loop.shLoop wrapper para batches grandes
anexos-ocr.mjsOCR diferido (procesa anexos marcados deferred-ocr)
anexos-summarize.mjsPasada IA posterior (sin tocar SECOP)
compress-extracted-text.mjsBackfill gzip de proceso_anexos.extracted_text (~76% ahorro). Idempotente.
rotate-logs.shRota /tmp/secop-*.log cuando superan 10 MB (5 rotaciones).
backup.shBackup selectivo de tablas de usuario + data/ (~700 KB/día, rotación 7d).

Variables de entorno

VariableDefaultDónde
SECOP_DB_PATHdb/secop.sqlite3Ruta de la BD
OLLAMA_URLhttp://localhost:11434Endpoint Ollama
OLLAMA_MODELqwen2.5:14bModelo a usar (override en cualquier script Node)
SERVICE_PORT5678Puerto del servicio Node
PORT4567Puerto Sinatra
BIND127.0.0.1Bind Sinatra
PUMA_WORKERS2 (macOS) / 0 (Windows)0 = single-mode (forzado en Windows: no hay fork())
PUMA_THREADS0:16min:max threads por worker
PUMA_WORKER_TIMEOUT60segundos sin progreso → kill worker
MAX_PAGE2000tope de paginación (cap de OFFSET)
PER_PAGE50filas por página en /listados
GZ_THRESHOLD2048bytes mínimos para comprimir extracted_text
LOG_MAX_BYTES10485760umbral de rotación (10 MB)
LOG_KEEP5rotaciones mantenidas
LIMIT10Procesos por batch (anexos.mjs)
MAX_BYTES26214400Tamaño máx por anexo (25 MB)
OCR_MAX_PAGES10Páginas a OCR-ear por PDF
SECOP_APP_TOKENToken Socrata (opcional, mejor rate-limit). Obtener en https://evp.colombiacompra.gov.co/ o https://www.datos.gov.co/profile (sección "App Tokens"). Guardar en ~/.secop_token con permisos 600 (lo carga import-daily.sh).
INCREMENTAL=1 para usar watermark en import-api.mjs
SKIP_LLM=1 para migración rápida sin Ollama
SKIP_OCR=1 para diferir OCR
SKIP_PAA / SKIP_PROCESOS=1 para saltar fuente en score-alertas.mjs
FORCE=1 para reprocesar incluso si ya OK

Esquema de la BD

Tablas públicas (re-importables desde SODA):

procesos              -- 2.99 M, una fila por numero_constancia (con FTS5 procesos_fts)
proceso_anexos        -- 1 fila por anexo descargado/analizado (extracted_text_gz comprimido)
paa_items             -- Plan Anual de Adquisiciones (anticipación de licitaciones)
unspsc_catalog        -- Clasificador UNSPSC oficial
import_state          -- watermark por dataset (SECOP I, II, PAA) para incremental

Tablas de usuario (cubiertas por bin/backup.sh):

empresa_perfiles, perfil_documentos, perfil_balances, perfil_desempates, perfil_certs
workflows, workflow_tareas
preferencias_vigilancia, sync_auto_log
ofertas_economicas, hojas_vida, enfoques_estrategicos
planes_desarrollo, plantillas_observaciones
alertas, notificaciones, firma_eventos, jobs

Troubleshooting

SíntomaCausa probableFix
Botón "Procesar con IA" no avanzaServicio Node no correnode bin/anexos-service.mjs
Análisis falla con "sesión expirada"Cookies SECOP II caducaronClic en "Renovar sesión SECOP II"
q=palabra no encuentra resultados que sí están en SECOP IIWatermark desactualizadoINCREMENTAL=1 node bin/import-api.mjs
Sinatra se cuelga en alguna páginaReinicia Puma`lsof -ti :4567 \xargs kill -9 y relanza bin/web`
Re-análisis muy lentoqwen2.5:14b en máquina chicaCambiar a OLLAMA_MODEL=qwen2.5:7b
BD muy grandeWAL acumuladosqlite3 db/secop.sqlite3 "PRAGMA wal_checkpoint(TRUNCATE)"

Notas operativas

  • El community.secop.gov.co requiere captcha reCAPTCHA v2 para cargar páginas de detalle. Nunca se evade: el usuario lo pasa manualmente una vez por sesión (vía botón "Renovar"). La sesión persiste varias horas hasta que SECOP la invalida.
  • La SODA API (datos.gov.co) es la fuente oficial sin captcha. Cubre toda la metadata. Los PDFs solo se obtienen vía portal.
  • La sesión Playwright (storageState) se guarda en .secop2-storage.json (gitignored).
  • El análisis IA se procesa on-demand por proceso, no en bulk. Cada análisis quema ~5-25 min de CPU local.

Licencia y uso

Investigación académica de transparencia en contratación pública. Datos son públicos (Ley 1712 de 2014). No redistribuir el corpus completo sin acuerdo con Colombia Compra Eficiente.