Historie 1949–2022 bleibt in lawgit, bis der GPU-Lauf endet; Schnitt ohne Lücke. Co-authored-by: Cursor <cursoragent@cursor.com>
176 lines
5.9 KiB
Python
176 lines
5.9 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Historische BGBl-PDFs 1949–2022 von OffeneGesetze.de.
|
||
|
||
Der Bestand liegt bereits lokal in lawgit (`bgbl/bgbl1/1949`–`2022`, ~5500 PDFs).
|
||
Dieses Skript dupliziert ihn nicht. Es schreibt ein Quellen-Manifest und kann
|
||
einzelne Jahrgänge nachziehen, falls sie hier wirklich gebraucht werden.
|
||
|
||
python3 download_offenegesetze.py --hinweis
|
||
python3 download_offenegesetze.py --year 1949 # nur wenn nötig
|
||
python3 download_offenegesetze.py --from-year 1949 --to-year 1950
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import argparse
|
||
import time
|
||
from pathlib import Path
|
||
from urllib.parse import urlparse
|
||
|
||
from bgbl_util import (
|
||
BGBl_DIR,
|
||
LAWGIT_BGBL,
|
||
LAWGIT_ROOT,
|
||
META_DIR,
|
||
SESSION,
|
||
download_file,
|
||
fundstelle,
|
||
issue_id,
|
||
save_json,
|
||
)
|
||
|
||
API_URL = "https://api.offenegesetze.de/v1/veroeffentlichung/"
|
||
MEDIA_BASE = "https://media.offenegesetze.de"
|
||
HISTORIE_BIS = 2022
|
||
HISTORIE_VON = 1949
|
||
|
||
|
||
def fetch_api(params: dict) -> list[dict]:
|
||
results = []
|
||
url = API_URL
|
||
params = {**params, "limit": 100}
|
||
while url:
|
||
r = SESSION.get(url, params=params if url == API_URL else None, timeout=60)
|
||
r.raise_for_status()
|
||
data = r.json()
|
||
results.extend(data.get("results") or [])
|
||
url = data.get("next")
|
||
params = None
|
||
time.sleep(0.15)
|
||
return results
|
||
|
||
|
||
def schreibe_hinweis() -> Path:
|
||
"""Kein PDF-Duplikat: nur dokumentieren, wo die Historie schon liegt."""
|
||
pdfs = 0
|
||
jahre: list[str] = []
|
||
quelle = LAWGIT_BGBL / "bgbl1"
|
||
if quelle.is_dir():
|
||
jahre = sorted(p.name for p in quelle.iterdir() if p.is_dir() and p.name.isdigit())
|
||
pdfs = sum(1 for p in quelle.rglob("*.pdf"))
|
||
|
||
manifest = {
|
||
"quelle": "offenegesetze.de",
|
||
"api": API_URL,
|
||
"jahre": f"{HISTORIE_VON}–{HISTORIE_BIS}",
|
||
"schnitt": "2022 endet hier; ab 2023 nur noch recht.bund.de (download_rechtbund.py)",
|
||
"lawgit_bestand": {
|
||
"pfad": str(quelle) if quelle.exists() else None,
|
||
"jahre": f"{jahre[0]}–{jahre[-1]}" if jahre else None,
|
||
"pdfs": pdfs,
|
||
"hinweis": (
|
||
"Nach Archivierung von lawgit diese PDFs hierher übernehmen "
|
||
"(kopieren oder verschieben), nicht ein zweites Mal von OffeneGesetze laden."
|
||
),
|
||
},
|
||
"nicht_duplizieren": True,
|
||
}
|
||
dest = META_DIR / "offenegesetze-quelle.json"
|
||
save_json(dest, manifest)
|
||
print("Historie 1949–2022: OffeneGesetze.de")
|
||
print(f" Schnitt: 2022 letzte OffeneGesetze-Jahr, 2023+ nur recht.bund.de")
|
||
if pdfs:
|
||
print(f" Bestand in lawgit: {pdfs} PDFs ({jahre[0]}–{jahre[-1]})")
|
||
print(f" Pfad: {quelle}")
|
||
print(" Keine Kopie angelegt — nach Archivierung von lawgit hierher ziehen.")
|
||
else:
|
||
print(f" lawgit-Bestand nicht gefunden unter {LAWGIT_ROOT}")
|
||
print(" Einzelne Jahre: python3 download_offenegesetze.py --year 1949")
|
||
print(f" Manifest: {dest}")
|
||
return dest
|
||
|
||
|
||
def download_year(kind: str, year: int) -> list[dict]:
|
||
if year > HISTORIE_BIS:
|
||
print(f"{year} gehört zu recht.bund.de — download_rechtbund.py nutzen")
|
||
return []
|
||
if year < HISTORIE_VON:
|
||
print(f"{year}: OffeneGesetze beginnt {HISTORIE_VON}")
|
||
return []
|
||
|
||
print(f"Lade Metadaten {kind} {year} …")
|
||
items = fetch_api({"year": year, "kind": kind})
|
||
normiert = []
|
||
for raw in items:
|
||
number = raw.get("number")
|
||
page = raw.get("page")
|
||
normiert.append(
|
||
{
|
||
"id": raw.get("id") or issue_id(kind, year, number or 0),
|
||
"kind": kind,
|
||
"year": year,
|
||
"number": number,
|
||
"page": page,
|
||
"date": (raw.get("date") or "")[:10] or None,
|
||
"title": raw.get("title"),
|
||
"titel": raw.get("title"),
|
||
"document_url": raw.get("document_url"),
|
||
"url": raw.get("url"),
|
||
"quelle": "offenegesetze",
|
||
"fundstelle": fundstelle(kind, year, number or 0, page),
|
||
}
|
||
)
|
||
save_json(META_DIR / f"{kind}-{year}.json", normiert)
|
||
print(f" {len(normiert)} Veröffentlichungen")
|
||
|
||
archive_url = f"{MEDIA_BASE}/{kind}/{year}.tar.bz2"
|
||
archive_path = BGBl_DIR / "archives" / f"{kind}-{year}.tar.bz2"
|
||
print(f"Lade Archiv {archive_url} …")
|
||
download_file(archive_url, archive_path)
|
||
if archive_path.exists() and archive_path.stat().st_size > 0:
|
||
print(f" Archiv liegt unter {archive_path} (nicht automatisch entpackt)")
|
||
else:
|
||
print(" Kein Jahresarchiv, lade Einzel-PDFs …")
|
||
seen = set()
|
||
for item in items:
|
||
doc = (item.get("document_url") or "").split("#")[0]
|
||
if not doc or doc in seen:
|
||
continue
|
||
seen.add(doc)
|
||
name = Path(urlparse(doc).path).name
|
||
dest = BGBl_DIR / kind / str(year) / name
|
||
print(f" PDF {name}")
|
||
download_file(doc, dest)
|
||
time.sleep(0.2)
|
||
return normiert
|
||
|
||
|
||
def main() -> None:
|
||
parser = argparse.ArgumentParser(description="BGBl 1949–2022 von OffeneGesetze.de")
|
||
parser.add_argument("--hinweis", action="store_true", help="Nur Quellen-Manifest, keine PDFs")
|
||
parser.add_argument("--year", type=int, action="append")
|
||
parser.add_argument("--from-year", type=int)
|
||
parser.add_argument("--to-year", type=int)
|
||
parser.add_argument("--kind", choices=["bgbl1", "bgbl2"], default="bgbl1")
|
||
args = parser.parse_args()
|
||
|
||
META_DIR.mkdir(parents=True, exist_ok=True)
|
||
|
||
years = list(args.year or [])
|
||
if args.from_year:
|
||
years.extend(range(args.from_year, (args.to_year or HISTORIE_BIS) + 1))
|
||
years = sorted(set(y for y in years if HISTORIE_VON <= y <= HISTORIE_BIS))
|
||
|
||
if args.hinweis or not years:
|
||
schreibe_hinweis()
|
||
if not years:
|
||
return
|
||
|
||
for year in years:
|
||
download_year(args.kind, year)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|