1
0
Files

102 lines
4.6 KiB
Python

#!/usr/bin/env python3
"""Ielādē Ministru kabineta sēdes lapas no TAP portāla kešā (.cache/, netiek glabāts git).
python3 tools/fetch.py # jaunākā MK sēde sēžu sarakstā
python3 tools/fetch.py 2026-10-13 # sēde šajā datumā (sēžu saraksta 1. lapā)
python3 tools/fetch.py --uuid 776813c2-… # sēde pēc TAP identifikatora
Ielādē: sēžu saraksta rindu (datums, laiks, nosaukums, protokola numurs), darba kārtību, katra jautājuma lēmumu tekstu
(MK sēdes protokollēmuma projekts, rīkojuma projekts) un TAP API ierakstu par tiesību akta projektu (veids, atbildīgā institūcija).
Pieprasījumi — pa vienam ar pauzi. Kešā — manifest.json ar katras lapas URL, sha256 un ielādes laiku.
"""
import datetime as dt
import hashlib
import json
import os
import re
import sys
import time
from lxml import html as H
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import tap # noqa: E402
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
LIST = tap.BASE + "/meetings/cabinet_ministers"
PAUSE = 0.5
def meeting_rows(raw):
"""Sēžu saraksta lapa → [{'uuid', 'start', 'title', 'protocol', 'protocol_url'}]."""
doc = H.fromstring(raw)
out = []
for r in doc.xpath('//*[starts-with(@data-url, "/meetings/cabinet_ministers/")]'):
m = re.match(r"/meetings/cabinet_ministers/([0-9a-f-]{36})$", r.get("data-url"))
if not m:
continue
t = tap.norm(r.text_content())
d = re.search(r"(\d{2})\.(\d{2})\.(\d{4})\.\s*(\d{1,2}):(\d{2})", t)
p = re.search(r"Nr\.\s*(\d+)", t)
a = r.xpath('.//a[starts-with(@href, "/meetings/protocols/")]')
title = re.sub(r"^\d{2}\.\d{2}\.\d{4}\.\s*\d{1,2}:\d{2}\s*", "", t)
title = re.sub(r"\s*Nr\.\s*\d+\s*$", "", title)
out.append({"uuid": m.group(1), "title": title, "protocol": int(p.group(1)) if p else None,
"protocol_url": tap.BASE + a[0].get("href") if a else None,
"start": f"{d.group(3)}-{d.group(2)}-{d.group(1)}T{int(d.group(4)):02d}:{d.group(5)}:00" if d else None})
return out
def main(argv):
rows = meeting_rows(tap.get(LIST))
if "--uuid" in argv:
uuid = argv[argv.index("--uuid") + 1]
row = next((r for r in rows if r["uuid"] == uuid), {"uuid": uuid, "title": None, "protocol": None, "start": None})
else:
want = next((a for a in argv if re.fullmatch(r"\d{4}-\d{2}-\d{2}", a)), None)
cand = [r for r in rows if r["start"] and (not want or r["start"].startswith(want))]
if not cand:
sys.exit(f"sēde nav atrasta sēžu saraksta 1. lapā: {want or 'jaunākā'}")
row = cand[0]
cache = os.path.join(ROOT, ".cache", row["uuid"])
os.makedirs(os.path.join(cache, "nodes"), exist_ok=True)
os.makedirs(os.path.join(cache, "api"), exist_ok=True)
files = {}
def save(rel, url, data):
with open(os.path.join(cache, rel), "wb") as f:
f.write(data)
files[rel] = {"url": url, "sha256": hashlib.sha256(data).hexdigest(),
"retrieved": dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat()}
url = f"{LIST}/{row['uuid']}"
raw = tap.get(url)
save("meeting.html", url, raw)
meeting = tap.parse_meeting(raw)
n_docs = n_api = 0
for sec in meeting["sections"]:
for it in sec["items"]:
for label, href in it["documents"]:
m = re.search(r"/structuralizer/data/nodes/([0-9a-f-]{36})/preview", href or "")
if label in tap.DECISION_DOCS and m:
time.sleep(PAUSE)
save(f"nodes/{m.group(1)}.html", tap.BASE + href, tap.get(tap.BASE + href))
n_docs += 1
if it["legal_act"]:
time.sleep(PAUSE)
api = f"{tap.BASE}/api/v1/legal_acts/{it['legal_act']}?include=legal_act_type,policy_areas"
try:
save(f"api/{it['legal_act']}.json", api, tap.get(api, accept="application/vnd.api+json"))
n_api += 1
except Exception as e: # noqa: BLE001 — slēgtiem projektiem API var neatbildēt
print(f"API {it['tap']}: {e}", file=sys.stderr)
with open(os.path.join(cache, "manifest.json"), "w", encoding="utf-8") as f:
json.dump({"meeting": row, "files": files}, f, ensure_ascii=False, indent=1)
items = sum(len(s["items"]) for s in meeting["sections"])
print(f"{row['start']} {row['title']}: {items} jautājumi, {n_docs} lēmumu teksti, {n_api} API ieraksti -> {cache}")
print(row["uuid"])
if __name__ == "__main__":
main(sys.argv[1:])