--- name: lobby-meetings description: Lobbyist-meeting disclosures of the PM, Government Office and every ministry (quarterly XLSX/HTML). module: lobi.mjs execution: code --- # Government Office and Ministry Lobby Meetings Since the 2021 lobistidega suhtlemise hea tava, EVERY ministry publishes its ministers' and top officials' (kantsler, asekantsler, ministri nõunik) lobbyist meetings on its own site, at least quarterly. Bills are drafted in ministries, so for "who lobbied on this law" the drafting ministry's page is the first stop — the PM register covers only the Government Office. ## Retrieve **Use `lobbyMeetings()` (below) — do not read the pages or workbooks by hand.** One call resolves the body, picks the quarter's own workbook or the page's disclosure tables, normalises every published column layout, filters by the window and **counts the meetings in code**. ```js const r = await lobbyMeetings("riigikantselei", { year: 2026, quarter: 1 }); show(r.totals); // { meetings: 21, firstDate: …, lastDate: … } show(r.rows.slice(0, 5)); ``` **The count is the thing this module exists for.** State `totals.meetings`. Counting the rows you printed is a measured failure, not a hypothetical: runs have answered 20, 22 and 20 for a quarter the register records 21 in, one of them printing a 21-row table under a sentence that said 20, and an earlier one said 68 by counting `` rows on the page past the quarter's own section. `body` accepts a key from `lobbyBodies()`, an Estonian name ("Rahandusministeerium"), or a ministry domain — an unknown domain is resolved by trying `/lobitegevus`, `/lobistid`, `/lobistidega-kohtumised` in turn. - `via` says which route ran: `xlsx` (a workbook covering the window) or `html` (the page's own tables, for the bodies that publish no workbook). - `totals.undated` counts rows the body published with no usable date — siseministeerium.ee has cells reading "Jaanuar". They are left out of the window rather than assumed into it. - `read` lists every URL fetched; `source` is the one to cite, `page` is the disclosure page a citizen can open. ## Access The nine bodies below were opened and confirmed to carry the disclosure header row on **2026-08-01**; `lobbyBodies()` returns the same list with the resolved URLs, so a lookup costs no redirect. - Government Office / PM: `riigikantselei.ee/…/lobistidega-kohtumised` — quarterly XLSX, one file per quarter. This is the only body whose page ALSO renders every past quarter as an HTML table, which is where the 68-row over-count came from. - Ministries run one shared Drupal platform: `https:///lobitegevus`, else `/lobistid` or `/lobistidega-kohtumised`. | Body | Format (2026-08-01) | | --- | --- | | Rahandusministeerium (`fin.ee`) | one `Lobiregister -veebi.xlsx` per year, plus the current quarter as an HTML table | | Justiits- ja Digiministeerium (`justdigi.ee`) | per-quarter HTML accordions; older quarters as XLSX with one sheet per year | | Majandus- ja Kommunikatsiooniministeerium (`mkm.ee`) | quarterly HTML accordions only; no XLSX since 2023 | | Sotsiaalministeerium (`sm.ee`) | monthly HTML for the current year; yearly XLSX for the archive | | `hm.ee`, `kliimaministeerium.ee`, `agri.ee`, `siseministeerium.ee` | HTML tables, quarterly or yearly | **Not every ministry publishes.** `kul.ee`, `kaitseministeerium.ee` and `vm.ee` answered 404 on all three aliases (checked 2026-08-01) — that is an absent disclosure, not a broken page, and `lobbyMeetings()` says so by name. If you ever read a page or workbook directly, the workbook has to arrive as BYTES: an XLSX is a ZIP, so anything that decodes the response as text destroys it (the archive's `fetchBytes` is what does this; `get()` calls `res.text()` and will not). **Reading it then needs something that can open a ZIP and parse a spreadsheet** — the archive's `xlsxRows` does it with no dependency, and any spreadsheet library you already have does it too. Excel date serials such as `45420` come back from `xlsxRows` already converted; whatever you use, state the date and never the serial. On fin.ee the workbook links sit inside an embedded JSON widget where the quote is `"` and every slash is `\/`, so a plain `href` scan yields a path that 404s. ## Return - One row per disclosed meeting: body, date, official (with post), lobbyist and organisation, subject, and the URL the rows came from. - Preserve names and organisation labels exactly as published. - No two bodies use the same column headings — the module maps them by name, so quote its `official`/`lobbyist` fields rather than a column position. ## Limits - Cadence and layout differ per ministry (quarterly, monthly, yearly cumulative) and publication lags: on 2026-07-27 MKM's newest accordion was still 2026 I kvartal. An empty quarter can be a real statement — fin.ee: "2026. II kvartalis kohtumisi huvigruppidega ei toimunud." - File naming dates do not always equal the covered quarter or revision date. - **Absence from a disclosure is not evidence that no contact occurred.** The hea tava is a government guideline, not statute: it binds ministers and top officials, not every ministry specialist, and no one in the Riigikogu. Committee-stage lobbying appears nowhere in ANY of these pages. Worked example (verified 2026-07-26): in 2024 II kvartal the PM register lists 6 meetings and no food or beverage contact — in the exact quarter when four industry associations and the law firm Sorainen were working the sugared-drinks tax bill through the Rahanduskomisjon, all of it public in the bill's own document trail. Ministry pages close the drafting-stage part of that gap; for who entered a bill's parliamentary debate, still read the bill's opinion documents via `riigikogu-open-data` and ministry correspondence via `ministry-document-registries`. - **We cannot open the workbooks with plain requests.** Several bodies publish a quarter only as an XLSX, and the bodies that also render HTML put every past quarter on one page — which is where the 68-row over-count came from — so without a runtime to unpack the file and filter by date, link the disclosure page and do not state a total. - Route by who met whom: the PM register for the prime minister and riigisekretär, the drafting ministry's page for a bill under preparation, the bill's own document trail for the full picture. ## Verify - A retrieval counts only when `totals.meetings` is a number and `rows` carries a date and a lobbyist for each entry stated. `meetings: 0` with a `via` and a `source` is a real finding — an empty quarter happens and fin.ee says so in words ("2026. II kvartalis kohtumisi huvigruppidega ei toimunud"). - Ground truth, re-verified live 2026-08-01 with `lobbyMeetings()`: Riigikantselei 2026 I kvartal = **21** meetings (2026-01-07 … 2026-03-27, via the quarter's own XLSX), Rahandusministeerium 2026 I kvartal = **5** (via `Lobiregister 2026-veebi.xlsx`). The same quarter counted off the Riigikantselei page's HTML instead spans ten quarterly tables and 152 rows, which is the over-count this recipe's module exists to make impossible. - Never state a total the module did not compute. If you read a page by hand, require its disclosure header row (Kuupäev / Kohtumise teema / Ametiisiku nimi / Lobisti või tema esindatava nimi) before quoting rows from it — and note that four of the nine bodies word those headings differently. ## Module API lobbyMeetings(body, {year, quarter, from, to}) -> {source, page, body, period, via, read, totals, rows, note}