Initialisiere Explor.Code.Law mit RII-Pipeline und Strato-Deploy.
Some checks failed
Deploy explore / deploy (push) Failing after 18s

Entscheidungen als Markdown, Normen-Index und schlankes Git-Sync nach /opt/explore (Gitea Actions, ohne Docker).

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-17 12:03:40 +02:00
commit bdaa21dc31
30 changed files with 2652 additions and 0 deletions

32
tests/fixtures/bgh_ix_zr_72_20.xml vendored Normal file
View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dokument SYSTEM "https://www.rechtsprechung-im-internet.de/dtd/v1/rii-dok.dtd">
<dokument>
<doknr>KORE000000001</doknr>
<ecli>ECLI:DE:BGH:2021:060521UIXZR72.20.0</ecli>
<gertyp>BGH</gertyp>
<gerort>Karlsruhe</gerort>
<spruchkoerper>IX. Zivilsenat</spruchkoerper>
<entsch-datum>2021-05-06</entsch-datum>
<aktenzeichen>IX ZR 72/20</aktenzeichen>
<doktyp>Urteil</doktyp>
<norm>§ 133 InsO
§ 242 BGB</norm>
<titelzeile><P>Vorsatzanfechtung</P></titelzeile>
<leitsatz>
<P>Die erkannte Zahlungsunfähigkeit allein trägt den Vorsatz nicht.</P>
</leitsatz>
<tenor>
<P>Auf die Revision wird das Urteil aufgehoben.</P>
</tenor>
<tatbestand>
<P>Die Klägerin verlangt Rückgewähr.</P>
</tatbestand>
<entscheidungsgruende>
<P>Die Revision ist begründet.</P>
<P>I. Das Berufungsgericht hat angenommen, der Anfechtungsgegner habe vorsätzlich gehandelt.</P>
</entscheidungsgruende>
<vorinstanz>
<gericht>OLG Hamm</gericht>
<aktenzeichen>I-27 U 12/19</aktenzeichen>
</vorinstanz>
</dokument>

25
tests/fixtures/bverfg_1_bvr_1585_19.xml vendored Normal file
View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dokument SYSTEM "https://www.rechtsprechung-im-internet.de/dtd/v1/rii-dok.dtd">
<dokument>
<doknr>KVRE000000002</doknr>
<ecli>ECLI:DE:BVerfG:2020:rs20200505.1bvr158519</ecli>
<gertyp>BVerfG</gertyp>
<gerort>Karlsruhe</gerort>
<spruchkoerper>1. Senat</spruchkoerper>
<entsch-datum>20200505</entsch-datum>
<aktenzeichen>1 BvR 1585/19</aktenzeichen>
<doktyp>Beschluss</doktyp>
<norm>Art. 2 Abs. 1 GG
Art. 1 Abs. 1 GG
§ 31 BVerfGG</norm>
<titelzeile><P>Recht auf informationelle Selbstbestimmung</P></titelzeile>
<leitsatz>
<P>Das allgemeine Persönlichkeitsrecht schützt auch vor der unkontrollierten Weitergabe von Daten.</P>
</leitsatz>
<tenor>
<P>Die Verfassungsbeschwerde wird nicht zur Entscheidung angenommen.</P>
</tenor>
<gruende>
<P>Die Annahmevoraussetzungen des § 93a Abs. 2 BVerfGG liegen nicht vor.</P>
</gruende>
</dokument>

192
tests/test_rii.py Normal file
View File

@@ -0,0 +1,192 @@
#!/usr/bin/env python3
"""Offline-Tests für rii_to_markdown und index_normen."""
from __future__ import annotations
import sys
import tempfile
import unittest
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
from rii_to_markdown import ( # noqa: E402
az_slug,
convert_xml_to_markdown_file,
ebene_for,
parse_norm_string,
parse_rii_xml,
relative_path,
to_markdown,
)
from index_normen import ( # noqa: E402
lawgit_path_for,
map_gesetz_slug,
parse_frontmatter_normen,
)
FIXTURES = Path(__file__).parent / "fixtures"
class TestEbene(unittest.TestCase):
def test_bverfg(self):
self.assertEqual(ebene_for("BVerfG"), "verfg")
def test_bgh(self):
self.assertEqual(ebene_for("BGH"), "bund")
def test_olg(self):
self.assertEqual(ebene_for("OLG München"), "olg")
class TestParseNorm(unittest.TestCase):
def test_paragraph_bgb(self):
n = parse_norm_string("§ 242 BGB")
self.assertEqual(n["gesetz"], "bgb")
self.assertEqual(n["paragraph"], "242")
self.assertEqual(n["roh"], "§ 242 BGB")
def test_artikel_gg(self):
n = parse_norm_string("Art. 2 Abs. 1 GG")
self.assertEqual(n["gesetz"], "gg")
self.assertEqual(n["paragraph"], "2")
def test_comma_separated_norms(self):
from rii_to_markdown import collect_normen
import xml.etree.ElementTree as ET
root = ET.fromstring(
"<dokument><norm>§ 20 StGB, § 224 StGB, Art. 2 Abs. 1 GG</norm></dokument>"
)
norms = collect_normen(root)
roh = {n["roh"] for n in norms}
self.assertIn("§ 20 StGB", roh)
self.assertIn("§ 224 StGB", roh)
self.assertIn("Art. 2 Abs. 1 GG", roh)
gg = next(n for n in norms if n["gesetz"] == "gg")
self.assertEqual(gg["paragraph"], "2")
class TestBghFixture(unittest.TestCase):
def setUp(self):
xml = (FIXTURES / "bgh_ix_zr_72_20.xml").read_text(encoding="utf-8")
self.parsed = parse_rii_xml(xml)
def test_parses(self):
self.assertIsNotNone(self.parsed)
def test_meta(self):
m = self.parsed["meta"]
self.assertEqual(m["ebene"], "bund")
self.assertEqual(m["gericht"], "BGH")
self.assertEqual(m["aktenzeichen"], "IX ZR 72/20")
self.assertEqual(m["datum"], "2021-05-06")
self.assertEqual(m["doknr"], "KORE000000001")
self.assertEqual(m["doktyp"], "Urteil")
self.assertEqual(m["quelle"], "rii")
def test_normen(self):
roh = {n["roh"] for n in self.parsed["meta"]["normen"]}
self.assertIn("§ 133 InsO", roh)
self.assertIn("§ 242 BGB", roh)
bgb = next(n for n in self.parsed["meta"]["normen"] if n["gesetz"] == "bgb")
self.assertEqual(bgb["paragraph"], "242")
def test_body(self):
b = self.parsed["body"]
self.assertIn("Zahlungsunfähigkeit", b["leitsatz"])
self.assertIn("aufgehoben", b["tenor"])
self.assertIn("Rückgewähr", b["tatbestand"])
self.assertIn("begründet", b["gruende"])
def test_vorinstanz(self):
self.assertTrue(any("OLG Hamm" in v for v in self.parsed["meta"]["vorinstanz"]))
def test_markdown_sections(self):
md = to_markdown(self.parsed)
self.assertIn("ebene: \"bund\"", md)
self.assertIn("## Leitsatz", md)
self.assertIn("## Tenor", md)
self.assertIn("## Tatbestand", md)
self.assertIn("## Gründe", md)
self.assertIn("gesetz: \"bgb\"", md)
def test_path(self):
rel = relative_path(self.parsed["meta"])
self.assertEqual(rel, Path("bgh/2021/ix-zr-72-20.md"))
def test_write_file(self):
xml = (FIXTURES / "bgh_ix_zr_72_20.xml").read_bytes()
with tempfile.TemporaryDirectory() as tmp:
out = convert_xml_to_markdown_file(xml, tmp)
self.assertIsNotNone(out)
self.assertTrue(out.exists())
self.assertEqual(out.name, "ix-zr-72-20.md")
class TestBverfgFixture(unittest.TestCase):
def setUp(self):
xml = (FIXTURES / "bverfg_1_bvr_1585_19.xml").read_text(encoding="utf-8")
self.parsed = parse_rii_xml(xml)
def test_ebene_verfg(self):
self.assertEqual(self.parsed["meta"]["ebene"], "verfg")
self.assertEqual(self.parsed["meta"]["gericht"], "BVerfG")
def test_datum_yyyymmdd(self):
self.assertEqual(self.parsed["meta"]["datum"], "2020-05-05")
def test_gruende_from_gruende_tag(self):
self.assertIn("Annahmevoraussetzungen", self.parsed["body"]["gruende"])
def test_path(self):
rel = relative_path(self.parsed["meta"])
self.assertEqual(rel, Path("bverfg/2020/1-bvr-1585-19.md"))
def test_norm_gg(self):
gesetze = {n["gesetz"] for n in self.parsed["meta"]["normen"]}
self.assertIn("gg", gesetze)
class TestAzSlug(unittest.TestCase):
def test_simple(self):
self.assertEqual(az_slug("VIII ZR 1/24"), "viii-zr-1-24")
def test_long_with_doknr(self):
long_az = "A " + "B " * 60 + "1/24"
slug = az_slug(long_az, "KORE123")
self.assertLessEqual(len(slug), 80)
self.assertTrue(slug.endswith("kore123") or "kore123" in slug)
class TestIndexNormen(unittest.TestCase):
def test_map_slug(self):
self.assertEqual(map_gesetz_slug("BGB"), "bgb")
self.assertEqual(map_gesetz_slug("GG"), "gg")
self.assertEqual(map_gesetz_slug("InsO"), "inso")
def test_lawgit_path(self):
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
(root / "laws_md" / "bgb").mkdir(parents=True)
(root / "laws_md" / "bgb" / "§242.md").write_text("# § 242\n", encoding="utf-8")
path, ok = lawgit_path_for(root, "bgb", "242")
self.assertTrue(ok)
self.assertTrue(str(path).endswith("§242.md"))
def test_frontmatter_normen(self):
md = '''---
doknr: "X"
normen:
- { gesetz: "bgb", paragraph: "242", roh: "§ 242 BGB" }
---
# Test
'''
norms = parse_frontmatter_normen(md)
self.assertEqual(len(norms), 1)
self.assertEqual(norms[0]["gesetz"], "bgb")
if __name__ == "__main__":
unittest.main()