Detta repository innehåller Python-script för att konvertera SFS-författningar (Svensk författningssamling) från JSON-format till Markdown med temporala taggar, HTML, Git och andra format.
Note
Detta är en del av SE-Lex, läs mer om projektet här.
SFS-författningar exporteras till https://github.com/se-lex/sfs och publiceras också som HTML på https://selex.se med stöd för EU:s juridiska identifieringsstandard (ELI).
- Se till att du har Python 3.11 eller senare installerat
- Installera nödvändiga beroenden:
pip install -r requirements.txtKonvertera JSON-filer med författningar till Markdown:
python sfs_processor.py --input sfs_json --output output/md --formats md-markersVerktyget kan generera författningar i flera olika format, beroende på användningsområde:
md-markers(förvalt): Markdown med semantiska<section>-taggar och selex-attribut för juridisk status och temporal hanteringmd: Rena Markdown-filer med normaliserade rubriknivåer, lämpliga för visning och läsning. Utgår från ett target-date (förvalt: dagens datum) för att visa hur lagen ser ut vid den tidpunkten
git: Exporterar författningar som Git-commits med historiska datum, vilket skapar en versionshistorik av lagstiftningen
html: Genererar HTML-filer i ELI-struktur (/eli/sfs/{år}/{nummer}/index.html) för webbpubliceringhtmldiff: Som HTML men inkluderar även separata versioner för varje ändringsförfattning
HTML-filer kan publiceras via:
- Cloudflare R2: Med
html-export-workflow.yml(kräver R2-credentials) - GitHub Pages: Med
github-pages-workflow.yml(enklare setup, kräver aktiverad GitHub Pages)
vector: Konverterar författningar till vektorembeddings för semantisk sökning och RAG-applikationer. Använder OpenAI:s text-embedding-3-large modell (3072 dimensioner) och stödjer lagring i PostgreSQL (pgvector), Elasticsearch eller JSON-fil.
Exempel på att kombinera flera format:
python sfs_processor.py --input sfs_json --output output --formats md,html,gitFör att konvertera författningar behöver du först ladda ner JSON-data:
python downloaders/download_sfs_docs.py --ids all --source rkrattsbaserpython downloaders/download_sfs_docs.py --ids "2024:675,2024:700" --source rkrattsbaserNedladdade filer sparas som standard i katalogen sfs_docs. Du kan ange annan katalog med --out parametern.
Konvertera alla JSON-filer i en katalog till Markdown:
python sfs_processor.py --input sfs_json --output output/md --formats md-markersBeroende på vilket format du väljer får du olika strukturer:
Markdown-filer med bevarad semantisk struktur genom <article> och <section>-taggar:
<article>: Omsluter hela författningen och kan innehålla temporala attribut (ikraft_datum, upphor_datum, etc.)<section class="avdelning">: Omsluter avdelningar (divisions) som överordnad strukturell enhet<section class="kapitel">: Omsluter kapitel som strukturell enhet med underliggande paragrafer<section class="paragraf">: Omsluter varje paragraf (§) som en avgränsad juridisk bestämmelse
<article selex:status="ikraft" selex:ikraft_datum="2025-01-01">
# Lag (2024:123) om exempel
<section class="avdelning" id="avd1">
## AVDELNING I. ALLMÄNNA BESTÄMMELSER
<section class="kapitel" id="inledande-bestammelser">
### Inledande bestämmelser
<section class="paragraf" id="inledande-bestammelser.1">
#### 1 §
Innehållet i paragrafen...
</section>
</section>
</section>
</article>Denna semantiska struktur bevarar dokumentets logiska uppbyggnad och möjliggör automatisk bearbetning, analys, och navigation av författningstexten. ID-attributen gör det möjligt att länka direkt till specifika rubriker och paragrafer (t.ex. #inledande-bestammelser.1). Taggarna kan även användas för CSS-styling och JavaScript-funktionalitet.
OBS! Trots HTML-taggarna är filerna fortfarande fullt läsbara som Markdown :)
Rena Markdown-filer med normaliserade rubriknivåer, utan section-taggar:
# Lag (2024:123) om exempel
## Inledande bestämmelser
### 1 §
Innehållet i paragrafen...
### 2 §
Mer innehåll...Detta format är lämpligt för enkel visning och läsning, utan metadata eller temporal hantering.
Förutom CSS-klasser använder <section>-taggarna även selex:-attribut för att hantera juridisk status och datum. Dessa attribut möjliggör filtrering av innehåll baserat på ikraftträdande- och upphörandedatum:
-
selex:status: Anger sektionens juridiska statusikraft: Sektionen innehåller ikraftträdanderegler (konverterat från t.ex. "/Träder i kraft I:2025-01-01")upphavd: Sektionen är upphävd (konverterad från ifall rubrik innehåller "upphävd" eller "/Upphör att gälla")
-
selex:ikraft_datum: Datum då sektionen träder ikraft (format: YYYY-MM-DD) -
selex:upphor_datum: Datum då sektionen upphör att gälla (format: YYYY-MM-DD) -
selex:ikraft_villkor: Villkor för ikraftträdande (när inget specifikt datum anges)
Exempel på selex-attribut:
<section class="kapitel" selex:status="ikraft" selex:ikraft_datum="2025-01-01">
### 1 § En paragraf
...
</section>
<section class="paragraf" selex:status="upphavd" selex:upphor_datum="2023-12-31">
#### 2 § En paragraf
...
</section>
<section class="kapitel" selex:status="ikraft" selex:ikraft_villkor="den dag regeringen bestämmer">
### 3 § Rubrik på villkorad ikraftträdande
...
</section>Dessa attribut används automatiskt av systemets datumfiltrering för att skapa versioner av författningar som gäller vid specifika tidpunkter. Sektioner med selex:upphor_datum som har passerat tas bort, och sektioner med selex:ikraft_datum som ännu inte har kommit tas bort från den aktuella versionen.
Systemet hanterar temporal processing (tidsbaserad filtrering) olika beroende på vilket format som används:
-
md-markers(förvalt): Bevarar selex-taggar och hoppar över temporal processing. Detta gör att alla temporal attribut behålls för senare bearbetning. Rekommenderas för att bevara all juridisk metadata. -
md: Tillämpar temporal processing med dagens datum som målpunkt. Detta är viktigt att förstå:- Upphävda bestämmelser (med
selex:upphor_datumföre dagens datum) tas bort - Bestämmelser som ännu inte trätt i kraft (med
selex:ikraft_datumefter dagens datum) tas bort - Selex-taggar tas bort efter filtrering
- Resultatet blir en "ren" Markdown-vy av hur lagen ser ut idag
- Obs: Eftersom temporal filtrering används automatiskt, kan innehåll försvinna om det är upphävt eller ej ikraftträtt
- Upphävda bestämmelser (med
-
git: Hoppar över temporal processing i huvudbearbetningen. Temporal hantering sköts separat i git-arbetsflödet för att skapa historiska commits. -
htmlochhtmldiff: Tillämpar temporal processing med dagens datum innan HTML-generering, liknandemd-format. -
vector: Tillämpar temporal processing med dagens datum (eller angivet--target-date) innan vektorgenerering. Detta säkerställer att endast gällande regelverk inkluderas i vektordatabasen.
För att se hur en lag såg ut vid ett specifikt datum:
# Se hur lagen såg ut 2023-01-01
python sfs_processor.py --input sfs_json --output output/md --formats md --target-date 2023-01-01Detta är användbart för att skapa historiska versioner eller för att förstå hur lagen såg ut vid en viss tidpunkt.
python sfs_processor.py [--input INPUT] [--output OUTPUT] [--formats FORMATS] [--filter FILTER] [--target-date DATE] [--no-year-folder] [--verbose]--input: Input-katalog med JSON-filer (default: "sfs_json")--output: Output-katalog för konverterade filer (default: "SFS")--formats: Utdataformat att generera, kommaseparerat. Stödjer: md-markers, md, git, html, htmldiff, vector (default: "md-markers")md-markers: Generera markdown-filer med section-taggar bevarademd: Generera rena markdown-filer utan section-taggargit: Aktivera Git-commits med historiska datumhtml: Generera HTML-filer i ELI-struktur (endast grunddokument)htmldiff: Generera HTML-filer i ELI-struktur med ändringsversionervector: Generera vektorembeddings för semantisk sökning
--filter: Filtrera filer efter år (YYYY) eller specifik beteckning (YYYY:NNN). Kan vara kommaseparerad lista.--target-date: Datum (YYYY-MM-DD) för temporal filtrering, baserat på selex-taggar. Används medmd,html,htmldiffochvectorformat för att filtrera innehåll baserat på giltighetsdatum. Om inte angivet används dagens datum. Exempel:--target-date 2023-01-01--no-year-folder: Skapa inte årbaserade undermappar för dokument--verbose: Visa detaljerad information om bearbetningen
--vector-backend: Backend för vektorlagring (default: "json")json: Spara till JSON-fil (för test/utveckling)postgresql: PostgreSQL med pgvector-extensionelasticsearch: Elasticsearch med dense_vector
--vector-chunking: Strategi för att dela upp dokument (default: "paragraph")paragraph: Dela per paragraf (§) - bevarar juridisk strukturchapter: Dela per kapitel - större kontextsection: Dela per selex-sektionsemantic: Semantiska gränser med överlappfixed_size: Fast tokenantal med överlapp
--embedding-model: Embedding-modell (default: "text-embedding-3-large")--vector-mock: Använd mock-embeddings för test utan OpenAI API-nyckel
Vektorformatet (--formats vector) konverterar författningar till vektorembeddings som kan användas för semantisk sökning, RAG-applikationer (Retrieval-Augmented Generation) och AI-assistenter.
- Temporal filtrering: Endast gällande regelverk inkluderas (samma som
md/htmlmode) - Intelligent chunking: Dokument delas upp på ett sätt som bevarar juridisk struktur
- Embedding-generering: Text konverteras till vektorer med OpenAI text-embedding-3-large
- Lagring: Vektorer sparas till vald backend med fullständig metadata
# Test med mock-embeddings (utan API-nyckel)
python sfs_processor.py --formats vector --vector-mock --filter 2024:100
# Produktion med OpenAI (kräver OPENAI_API_KEY miljövariabel)
python sfs_processor.py --formats vector --filter 2024
# Med PostgreSQL/pgvector backend
python sfs_processor.py --formats vector --vector-backend postgresql
# Med kapitel-chunking för större kontext
python sfs_processor.py --formats vector --vector-chunking chapter| Backend | Användning | Krav |
|---|---|---|
json |
Test/utveckling | Inga |
postgresql |
Produktion | PostgreSQL 12+ med pgvector |
elasticsearch |
Produktion | Elasticsearch 8.0+ |
Varje vektor-chunk innehåller:
document_id: Beteckning (t.ex. "2024:100")chapter: Kapitelreferens (t.ex. "1 kap.")paragraph: Paragrafreferens (t.ex. "1 §")departement: Ansvarigt departementeffective_date: Ikraftträdande-datum
Vi välkomnar bidrag från communityn! 🙌
- Läs CONTRIBUTING.md för riktlinjer om hur du bidrar
- Se DEVELOPMENT.md för utvecklardokumentation och arkitekturöversikt
- Kontakt: Martin Rimskog via e-post eller LinkedIn