Saltar a contenido

Modelo de datos

Postgres. Django ORM. El payload crudo de cada fuente se guarda en JSONB tal como llegó.

Por qué guardar el crudo

Reprocesar el normalizador sobre el histórico sin volver a descargar es una operación que se va a necesitar cada vez que se mejore una regla. Sin el crudo guardado, cada mejora cuesta una recarga completa contra APIs con cuota.

Diagrama

erDiagram
    fuentes ||--o{ ingestas : "produce"
    ingestas ||--o{ avisos : "contiene"
    fuentes ||--o{ avisos : "origina"
    proyectos ||--o{ avisos : "agrupa"
    proyectos ||--o{ proyecto_versiones : "historial"
    avisos ||--o{ decisiones : "evaluado en"
    avisos ||--o{ documentos : "adjunta"
    radares }o--|| paises : "ubicado en"
    adjudicaciones }o--|| empresas : "ganada por"

    fuentes {
        int id PK
        varchar nombre
        varchar carril
        varchar tipo
        text endpoint
        jsonb params
        varchar cadencia
        bool activo
        timestamptz ultimo_ok
        text watermark
    }
    ingestas {
        int id PK
        int fuente_id FK
        timestamptz iniciada
        varchar estado
        int registros
        text error
    }
    avisos {
        int id PK
        int fuente_id FK
        varchar id_nativo
        varchar hash
        varchar escalon
        varchar categoria_adquisicion
        timestamptz fecha_limite_utc
        int proyecto_id FK
        jsonb crudo
    }
    proyectos {
        int id PK
        varchar id_nativo
        varchar pais
        varchar estado
        jsonb crudo
        timestamptz visto_por_ultima_vez
    }
    decisiones {
        int id PK
        int aviso_id FK
        varchar decision
        varchar codigo_razon
        bool automatica
    }

Tablas

fuentes

Registro de fuentes. Agregar una fuente es insertar una fila, no editar el orquestador.

Campo Tipo Descripción
nombre varchar Identificador legible
carril varchar A, B, C — una fuente puede alimentar varios, se usa fila por carril
tipo varchar api_json, api_sql, rss, scrape, busqueda
endpoint text URL base
metodo varchar GET / POST
params jsonb Parámetros de la consulta
cadencia varchar diaria, semanal
idioma, pais varchar Para consultas multilingües y por país
activo bool Apagar una fuente sin borrarla
watermark text Última fecha o token procesado
ultimo_ok timestamptz Última corrida con resultados

ingestas

Una fila por corrida por fuente. Es la base de la observabilidad.

avisos — carriles A y C

Append-only. Clave de deduplicación: fuente_id + id_nativo.

hash es un hash del contenido normalizado, aparte del ID. Sirve para detectar modificaciones —prórroga de plazo, adenda— y reportarlas como cambio, no como aviso nuevo.

crudo guarda el payload de la fuente sin tocar.

proyectos y proyecto_versiones — carril B

Upsert, no append. Cada corrida compara el snapshot nuevo contra la última versión y guarda una fila en proyecto_versiones solo si cambió algo.

Lo que se reporta es el diff

El reporte del carril B muestra transiciones de estado, no filas nuevas. Un proyecto que sigue igual desde hace tres meses no aparece.

decisiones

Toda evaluación, incluidos los descartes automáticos.

Campos: aviso_id, fecha, decision (bid / no_bid / ignorar), codigo_razon, quien, nota_libre, automatica.

Es el activo de largo plazo

En un año habrá un par de cientos de decisiones etiquetadas y se podrán revisar patrones a mano. Vale más que el modelo que no vamos a poder entrenar.

documentos

PDF descargados y su texto extraído. Se guarda el texto para no volver a descargar ni re-extraer.

Clave de caché: hash del archivo. El scoring con LLM también se cachea por ese hash.

radares, paises, empresas, adjudicaciones

El mapa de base instalada. Se pueblan en fase 3 desde WRD y desde el histórico de adjudicaciones del BID y el Banco Mundial.

Vista derivada clave: países con dos o más fabricantes distintos en el parque.

Reglas transversales

  • Todas las fechas en timestamptz en UTC, más el string original de la fuente en un campo aparte
  • Ningún campo de fecha límite es obligatorio: los avisos generales y las EOI frecuentemente no la traen
  • Índice de texto completo de Postgres sobre documentos.texto