Add verified data-access findings + MVP plan
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
# radar-contratos — data access + MVP plan
|
||||
|
||||
> Living planning doc. The data-access question is **resolved** (verified June 2026).
|
||||
|
||||
## Data access — VERIFIED ✓
|
||||
|
||||
**Primary source: per-year XLSX bulk dumps on dados.gov.pt.**
|
||||
Dataset: `contratos-publicos-portal-base-impic-contratos-de-2012-a-2026` (org: IMPIC).
|
||||
|
||||
- One **`.xlsx` per year, 2012 → 2026**, all populated (16 MB in 2012 → 67 MB in 2025;
|
||||
2026 is partial/in-progress). Full set ≈ **600 MB**.
|
||||
- Refresh cadence: **biweekly** (dataset `last_update` was within days of checking).
|
||||
- **No auth, no scraping** — plain HTTPS file downloads.
|
||||
- ⚠️ The dataset also ships a `.zip`/JSON mirror per year, but **many are empty/broken**
|
||||
(2014–2018, 2025, 2026 zips were 0 bytes / `[]`). **Use the XLSX as source of truth.**
|
||||
|
||||
**Companion datasets (same IMPIC family, also bulk):**
|
||||
- `...-entidades` → `entidades.xlsx` (~13 MB) — canonical entity list (buyers/suppliers).
|
||||
- `...-anuncios-de-2012-a-2026` → announcements/tenders JSON (~15 MB/yr). Note: **only
|
||||
*tendered* procedures announce** — ajuste direto usually has no announcement, so this is
|
||||
a supplement, not the core.
|
||||
- `...-modificacoes-contratuais-...` → contract amendments JSON (price/term changes).
|
||||
|
||||
**Fresher data later:** IMPIC offers an official **BASE API** (token via IMPIC Helpdesk,
|
||||
daily updates) — defer; the biweekly XLSX is plenty for MVP.
|
||||
|
||||
### Contracts schema (39 cols) — the fields that matter
|
||||
|
||||
| Column | Use |
|
||||
|---|---|
|
||||
| `tipoprocedimento` | procedure type — `"Ajuste Direto Regime Geral"` etc. **(the core filter)** |
|
||||
| `adjudicante` | buyer, as `"NIF - name"` → split on `" - "` for entity key |
|
||||
| `adjudicatarios` | supplier(s), same `"NIF - name"` shape (can be multiple) |
|
||||
| `precoContratual`, `PrecoTotalEfetivo`, `precoBaseProcedimento` | values |
|
||||
| `dataCelebracaoContrato`, `dataPublicacao`, `dataDecisaoAdjudicacao`, `dataFechoContrato` | dates |
|
||||
| `objectoContrato`, `CPV` | what was bought (CPV = EU procurement category code) |
|
||||
| `fundamentAjusteDireto`, `fundamentacao` | legal basis cited for the direct award |
|
||||
| `NUTs`, `LocalExecucao` | region |
|
||||
| `concorrentes` | competitors (often empty for ajuste direto) |
|
||||
| `Ano`, `prazoExecucao`, `regime`, `Lotes` | year, term, legal regime, lots |
|
||||
|
||||
## MVP scope — one lens: `ajuste direto` red flags
|
||||
|
||||
Keep it narrow. Ship **one analysis fully** before adding breadth.
|
||||
|
||||
1. **Contract-splitting** — same `adjudicante` → same `adjudicatario` (by NIF), same/near
|
||||
`CPV`, clustered in a short window, each contract just under the legal ajuste-direto
|
||||
value threshold, summing to *above* it. The law forbids splitting to dodge a tender.
|
||||
⚠️ **Confirm current legal thresholds** (CCP, amended by Lei 30/2021 — values differ for
|
||||
bens/serviços vs. empreitadas, and changed over time → threshold must be date-aware).
|
||||
2. **Repeat winners** — supplier's share of a given buyer's ajuste-direto spend; flag high
|
||||
concentration / suppliers that win disproportionately from one entity.
|
||||
3. **Timing** — award bursts before elections or at year-end.
|
||||
|
||||
Each flag is a **question, not a verdict**: always deep-link the underlying contract, with
|
||||
"isto não é uma acusação" framing.
|
||||
|
||||
## Architecture (proposed)
|
||||
|
||||
```
|
||||
download yearly XLSX ──> parse ──> Postgres ──> SQL analyses ──> present
|
||||
(biweekly job) (the red-flag logic)
|
||||
```
|
||||
|
||||
- **The heavy lifting is SQL** (group-bys over a contracts table) — language-agnostic.
|
||||
- Proposed stack: **Elixir/Phoenix + LiveView + Postgres**, consistent with
|
||||
[`o-que-mudou`](../o-que-mudou); ingestion = download + xlsx parse + bulk upsert keyed on
|
||||
`idcontrato`. Open to a standalone ETL step if Elixir xlsx parsing is painful at 600 MB.
|
||||
- Entity table seeded from `entidades.xlsx`; NIF is the join key everywhere.
|
||||
|
||||
## Open questions
|
||||
|
||||
- **Legal thresholds, date-aware** — exact ajuste-direto / consulta-prévia ceilings per
|
||||
period and contract type. Make-or-break for splitting detection accuracy.
|
||||
- **`adjudicatarios` multiplicity** — contracts with multiple suppliers / lots; how to
|
||||
attribute value.
|
||||
- **Entity identity** — trust embedded NIF; reconcile name variants via `entidades.xlsx`.
|
||||
- **Initial load volume** — ~600 MB XLSX → estimate row count (likely 1.5–2M contracts) and
|
||||
confirm Postgres sizing (trivial) + parse time.
|
||||
|
||||
## Out of scope (later / other projects)
|
||||
|
||||
Cross-referencing winners against political donations, beneficial ownership, or officials'
|
||||
declared interests — that's the separate "conflict-of-interest engine" idea, not this MVP.
|
||||
Reference in New Issue
Block a user