Search, chat and research over parliamentary speeches and documents
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

91 lines
2.7 KiB

"""
Laddar ned motioner från riksdagens öppna data som ZIP-arkiv med JSON-filer.
Arkiven är uppdelade i fyraårsperioder (förankrade vid 1998; allt före ligger
i 1990-1997) och uppdateras dagligen:
https://data.riksdagen.se/dataset/dokument/mot-{range}.json.zip
Varje arkiv innehåller en JSON-fil per motion (dokumentstatus-kuvert med
metadata + fulltext som HTML). Extraheras till motioner/mot-{range}/.
Backfill (alla perioder 1990→): python scripts/download_motions.py
Används av sync_motions.py för daglig uppdatering av aktuell period.
"""
from pathlib import Path
import logging
import os
import sys
from io import BytesIO
from urllib.request import urlopen
from zipfile import ZipFile
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
import bootstrap # noqa: E402,F401 — sets cwd and sys.path to the project root
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
)
logger = logging.getLogger(__name__)
RANGES = [
"1990-1997",
"1998-2001",
"2002-2005",
"2006-2009",
"2010-2013",
"2014-2017",
"2018-2021",
"2022-2025",
]
URL_TEMPLATE = "https://data.riksdagen.se/dataset/dokument/mot-{r}.json.zip"
MOTIONS_DIR = "motioner"
def get_current_range(session_year: int) -> str:
"""Fyraårsperiod förankrad vid 1998; allt före 1998 ligger i 1990-1997."""
if session_year < 1998:
return "1990-1997"
start = session_year - ((session_year - 1998) % 4)
return f"{start}-{start + 3}"
def download_range(r: str, force: bool = False) -> str:
"""
Laddar ned och extraherar arkivet för en period till motioner/mot-{r}/.
Hoppar över om mappen redan är ifylld, om inte force=True (töms först).
"""
dir_path = os.path.join(MOTIONS_DIR, f"mot-{r}")
if os.path.exists(dir_path) and os.listdir(dir_path):
if not force:
logger.info(f"Skipping {dir_path}, already exists and is not empty.")
return dir_path
for f in os.listdir(dir_path):
os.remove(os.path.join(dir_path, f))
os.makedirs(dir_path, exist_ok=True)
url = URL_TEMPLATE.format(r=r)
logger.info(f"Downloading {url}{dir_path}")
with urlopen(url) as resp:
with ZipFile(BytesIO(resp.read())) as zf:
zf.extractall(dir_path)
count = len(os.listdir(dir_path))
logger.info(f"Extracted {count} files to {dir_path}")
return dir_path
def download_all(force: bool = False) -> None:
for r in RANGES:
download_range(r, force=force)
if __name__ == "__main__":
if len(sys.argv) > 1:
download_range(sys.argv[1], force="--force" in sys.argv)
else:
download_all()