Repository: openlegaldata/legal-reference-extraction

Issue:
False-Positive Extractions
Hallo Malte,

Bei der Verwendung des Referenzen-Extraktions-Tools sind mir einige "false-positive"-Beispiele aufgefallen. Hier ein kleiner Auszug:

'Ein Satz mit 2014 und 2014/20, und weiteren Sachen' -> '2014 und 2014/20' (case)
'Ein Satz mit 2000 bis 07/20, und weiteren Sachen' -> '2000 bis 07/20' (case)
'Ein Satz mit 2019 bis KW 44/20, und weiteren Sachen' -> '2019 bis KW 44/20' (case)
'Ein Satz mit 49364 Reifen 245/45, und weiteren Sachen' -> '49364 Reifen 245/45' (case)

Für Rückfragen stehe ich gerne zur Verfügung.

Viele Grüße,
Niklas

Discussion:
@malteos: Hi Niklas,

danke für den Hinweis. Das sieht so aus als müsste der Regex überarbeitet werden.

Falls du es selber fixen willst, ist ein PR sehr willkommen! Ansonsten wird es wahrscheinlich etwas dauern bis ich mich dransetzen kann.

Beste Grüße
Malte

Solution patch:
diff --git a/refex/data/file_number_codes.csv b/refex/data/file_number_codes.csv
index 535ab97..c484ec5 100644
--- a/refex/data/file_number_codes.csv
+++ b/refex/data/file_number_codes.csv
@@ -1,4 +1,3 @@
-A,Aufsichtsrechts
 I,Beurkundungen (AG)
 II,Sonstiges in der freiwilligen Gerichtsbarkeit (AG)
 III,Standesamtssachen (AG)
@@ -16,26 +15,30 @@ XVI,Adoptionssachen (AG)
 XV,Landwirtschaftssachen (AG)
 XVI,Adoptionssachen (AG)
 XVII,Betreuungssachen (AG)
+A,Berufungen etc. (OVG)
 ABR,Rechtsbeschwerden (BAG)
+AGH,Berufsrechtliche und verwaltungsrechtliche Verfahren (Anwaltsgerichtshof)
 AK,Aktenkontrolle für Haftprüfungsverfahren (BGH-Strafsachen)
 AktE,Anträge auf gerichtliche Entscheidung nach dem AktG, dem EGAktG und verwandten Gesetzen (LG)
+AktG,Freigabeverfahren nach dem Aktien- und Umwandlungsgesetz (OLG)
+AnwG, Berufsrechtliche Vefahren (Anwaltsgerich)
 AnwSt,Anträge auf Einleitung des anwaltsgerichtlichen Verfahrens und Beschwerden in Ordnungsmittelverfahren gegen Rechtsanwälte beim Bundesgerichtshof (BGH, GenBAnw.)
-AnwSt(B),Beschwerden gegen die Nichtzulassung der Revision und Beschwerden gegen Entscheidungen eines Anwaltsgerichtshofes (BGH, GenBAnw.)
-AnwSt(R),Revisionen gegen Urteile eines Anwaltsgerichtshofes (BGH, GenBAnw.)
+AnwSt (B),Beschwerden gegen die Nichtzulassung der Revision und Beschwerden gegen Entscheidungen eines Anwaltsgerichtshofes (BGH, GenBAnw.)
+AnwSt (R),Revisionen gegen Urteile eines Anwaltsgerichtshofes (BGH, GenBAnw.)
 AnwZ,Klagen über Entscheidungen in Zulassungssachen oder gegen sonstige Verwaltungsakte betreffend Rechtsanwälte beim Bundesgerichtshof (BGH)
-AnwZ(B),Beschwerden gegen Entscheidungen eines Anwaltsgerichtshofes (BGH)
-AnwZ(Brfg),Berufungen und Anträge auf Zulassung der Berufung gegen Entscheidungen eines Anwaltsgerichtshofes (BGH)
-AnwZ(P),Klagen über die Anfechtung von Wahlen und Beschlüssen der Rechtsanwaltskammer beim BGH und der Bundesrechtsanwaltskammer (BGH)
+AnwZ (B),Beschwerden gegen Entscheidungen eines Anwaltsgerichtshofes (BGH)
+AnwZ (Brfg),Berufungen und Anträge auf Zulassung der Berufung gegen Entscheidungen eines Anwaltsgerichtshofes (BGH)
+AnwZ (P),Klagen über die Anfechtung von Wahlen und Beschlüssen der Rechtsanwaltskammer beim BGH und der Bundesrechtsanwaltskammer (BGH)
 AR,Allgemeines Register (GenBAnw.)
-AR(Kart),Rechtsbeschwerden in Kartellbußgeldverfahren (GenBAnw.)
-AR(Ri),Allgemeines Register beim Dienstgericht des Bundes (BGH)
+AR (Kart),Rechtsbeschwerden in Kartellbußgeldverfahren (GenBAnw.)
+AR (Ri),Allgemeines Register beim Dienstgericht des Bundes (BGH)
 ARP,Allgemeines Register für Staatsschutzstrafsachen (GenBAnw.)
-AR(Enw),Rechtsbeschwerden in Bußgeldverfahren nach dem EnWG (GenBAnw.)
+AR (Enw),Rechtsbeschwerden in Bußgeldverfahren nach dem EnWG (GenBAnw.)
 ARs,Allgemeine Register und Gerichtsstandsbestimmungen (BGH-Strafsachen)
 ARV,Vorlagen nach §§ 80, 86 BetrVG (BAG)
-AR(Vollz),Vorlegungssachen in Strafvollzugssachen (BGH-Strafsachen)
-AR(VS),Entscheidungen über Justizverwaltungsakte (BGH-Strafsachen)
-AR(VZ),Entscheidungen über Justizverwaltungsakte (BGH-Zivilsachen)
+AR (Vollz),Vorlegungssachen in Strafvollzugssachen (BGH-Strafsachen)
+AR (VS),Entscheidungen über Justizverwaltungsakte (BGH-Strafsachen)
+AR (VZ),Entscheidungen über Justizverwaltungsakte (BGH-Zivilsachen)
 ARZ,Allgemeine Register und Gerichtsstandsbestimmungen (BGH-Zivilsachen)
 Ausl,Auslieferungssachen, Durchlieferungssachen (GenStA)
 AZA,Prozesskostenhilfeanträge außerhalb anhängiger Verfahren (BAG)
@@ -44,6 +47,7 @@ AZR,Revisionen (BAG)
 B,Mahnsachen (AG)
 Ba,Arbeitsgerichtliche Mahnsachen (ArbG)
 BAusl,Auslieferungssachen (GenBAnw.)
+BewÜberwR,Bewährungsüberwachung und Führungsaufsichtsüberwachung (Amtsgericht)
 BGns,Gnadensachen (GenBAnw.)
 BGs,Einzelne richterliche Anordnungen des Ermittlungsrichters (BGH-Strafsachen)
 BJs,Ermittlungsverfahren des Generalbundesanwalts beim Bundesgerichtshof (GenBAnw.)
@@ -77,6 +81,7 @@ C,Allgemeine Zivilsachen (AG)
 C,Rechtssache (EuGH) Beispiel: C-150/12: 150. Rechtssache (Rs.) im Jahr 2012
 Ca,Allgemeine Zivilsachen am Arbeitsgericht (ArbG)
 Cs,Strafbefehle (AG)
+D,Hauptsacheverfahren (OVG)
 DG,Dienstgericht für Richter (LG, OLG)
 DGH,Dienstgerichtshof für Richter (OLG)
 DR,Aufträge an Gerichtsvollzieher (GV)
@@ -120,6 +125,7 @@ JL,Aufträge der Justizbehörden an Gerichtsvollzieher (GV)
 Js,Ermittlungsverfahren (StA)
 K,Zwangsversteigerungssachen (AG)
 KLs,Zusatz bei Strafsachen vor der Großen Strafkammer (LG)
+KN,Bebauungspläne (OVG Schleswig)
 KRB,Rechtsbeschwerden in Kartellbußgeldverfahren (BGH)
 Ks,Zusatz bei Schwurgerichtssachen (LG)
 KVR,Rechtsbeschwerden in Kartellverwaltungssachen (BGH)
@@ -146,10 +152,10 @@ LwZR,Revisionen, Beschwerden gegen die Nichtzulassung der Revision und Anträge
 M,Allgemeine Zwangsvollstreckungssachen (AG)
 MR,Musterregister (AG)
 Ni,Patentnichtigkeitsverfahren (BPatG)
-NotSt(B),Beschwerden gegen Beschlüsse der Oberlandesgerichte in Disziplinarsachen gegen Notare (BGH)
-NotSt(Brfg),Berufungen und Anträge auf Zulassung der Berufung gegen Urteile der Oberlandesgerichte in Disziplinarsachen gegen Notare (BGH)
+NotSt (B),Beschwerden gegen Beschlüsse der Oberlandesgerichte in Disziplinarsachen gegen Notare (BGH)
+NotSt (Brfg),Berufungen und Anträge auf Zulassung der Berufung gegen Urteile der Oberlandesgerichte in Disziplinarsachen gegen Notare (BGH)
 NotZ,Verwaltungsstreitverfahren in Notarsachen und Beschwerden gegen Entscheidungen der Oberlandesgerichte (BGH)
-NotZ(Brfg),Berufungen und Anträge auf Zulassung der Berufung gegen Entscheidungen der Oberlandesgerichte in Notarsachen (BGH)
+NotZ (Brfg),Berufungen und Anträge auf Zulassung der Berufung gegen Entscheidungen der Oberlandesgerichte in Notarsachen (BGH)
 Ns,Berufungen in Strafsachen (LG)
 O,Allgemeine Zivilsachen 1. Instanz (LG)
 ObJs,Strafsachen 1. Instanz (GenStA)
@@ -158,21 +164,21 @@ OH,Anträge außerhalb anhängiger Verfahren der 1. Instanz (LG)
 OJs,Erstinstanzliche Strafsachen (GenStA)
 OWi,Bußgeldverfahren (AG)
 PatAnwZ,Verwaltungsstreitverfahren in Patentanwaltssachen; Berufungen und Anträge auf Zulassung der Berufung und Beschwerden gegen Entscheidungen der Oberlandesgerichte (BGH)
-PatAnwSt(B),Beschwerden gegen die Nichtzulassung der Revision und Beschwerden nach der PatAO (BGH, GenBAnw.)
-PatAnwSt(R),Revisionen nach der PatAO (BGH, GenBAnw.)
+PatAnwSt (B),Beschwerden gegen die Nichtzulassung der Revision und Beschwerden nach der PatAO (BGH, GenBAnw.)
+PatAnwSt (R),Revisionen nach der PatAO (BGH, GenBAnw.)
 PBvU,Plenarentscheidungen, § 16 BVerfGG (BVerfG)
 PK,Pachtkreditregister (AG)
 PLs,Strafprozessliste (Amtsanwalt)
 PR,Partnerschaftsregistersachen (AG)
 Ps,Berufungen in Privatklagesachen (LG)
 Qs,Beschwerden in Straf- und Bußgeldsachen (LG)
-RE-Miet,Rechtsentscheide in Mietsachen (OLG)
+REMiet,Rechtsentscheide in Mietsachen (OLG)
 RiSt,Disziplinarsachen und Anträge auf Einleitung oder Einstellung des förmlichen Disziplinarverfahrens, auf vorläufige Dienstenthebung, auf Einbehaltung von Dienstbezügen sowie auf Aufhebung dieser Maßnahmen gegen Richter und Staatsanwälte im Bundesdienst sowie Mitglieder des Bundesrechnungshofes (BGH)
-RiSt(B),Beschwerden gegen die Nichtzulassung der Revision und Beschwerden der Richter, Staatsanwälte und Mitglieder des Bundesrechnungshofes gegen Disziplinarverfügungen (BGH)
-RiSt(R),Revisionen in Disziplinarsachen nach dem Deutschen Richtergesetz (BGH)
+RiSt (B),Beschwerden gegen die Nichtzulassung der Revision und Beschwerden der Richter, Staatsanwälte und Mitglieder des Bundesrechnungshofes gegen Disziplinarverfügungen (BGH)
+RiSt (R),Revisionen in Disziplinarsachen nach dem Deutschen Richtergesetz (BGH)
 RiZ,Anträge betreffend Richter im Bundesdienst und Mitglieder des Bundesrechnungshofes auf gerichtliche Entscheidung im Versetzungs- und Prüfungsverfahren sowie auf vorläufige Untersagung der Amtsgeschäfte (BGH)
-RiZ(B),Beschwerden gegen die Nichtzulassung der Revision nach dem Deutschen Richtergesetz (BGH)
-RiZ(R),Revisionen in Versetzungs- und Prüfungsverfahren nach dem Deutschen Richtergesetz (BGH)
+RiZ (B),Beschwerden gegen die Nichtzulassung der Revision nach dem Deutschen Richtergesetz (BGH)
+RiZ (R),Revisionen in Versetzungs- und Prüfungsverfahren nach dem Deutschen Richtergesetz (BGH)
 RS,Zivilsachen (GenStA)
 S,Berufungen in Zivilsachen (LG)
 Sa,Berufungssachen (LArbG)
@@ -184,8 +190,8 @@ Ss (B),Rechtsbeschwerden in Bußgeldsachen (OLG)
 Ss (OWi),Rechtsbeschwerden in Bußgeldsachen (OLG)
 SSR,Seeschiffsregister (AG)
 StB,Beschwerden (BGH-Strafsachen)
-StbSt(B),Beschwerden gegen die Nichtzulassung der Revision und Beschwerden in berufsgerichtlichen Verfahren in Steuerberater- und Steuerbevollmächtigtensachen (BGH, GenBAnw.)
-StbSt(R),Revisionen in berufsgerichtlichen Verfahren in Steuerberater- und Steuerbevollmächtigtensachen (BGH, GenBAnw.)
+StbSt (B),Beschwerden gegen die Nichtzulassung der Revision und Beschwerden in berufsgerichtlichen Verfahren in Steuerberater- und Steuerbevollmächtigtensachen (BGH, GenBAnw.)
+StbSt (R),Revisionen in berufsgerichtlichen Verfahren in Steuerberater- und Steuerbevollmächtigtensachen (BGH, GenBAnw.)
 StE,Strafsachen vor den Oberlandesgerichten, in denen der Generalbundesanwalt beim Bundesgerichtshof Anklage erhoben hat (GenBAnw.)
 StR,Revisionen und Vorlegungssachen (BGH-Strafsachen, GenBAnw.)
 StrEs,Register für Entschädigungssachen nach dem StrEG (GenStA)
@@ -214,16 +220,16 @@ VRJs,Vollstreckungsregister für Jugendgerichtssachen (AG)
 VRs,Strafvollstreckungssachen (StA)
 Vs,Revisionen in Privatklagesachen (OLG)
 W,Beschwerden in Zivilsachen (OLG)
-W-(pat),Beschwerden in Patent-, Gebrauchsmuster-, Sortenschutz- und Warenzeichensachen (BPatG)
+W (pat),Beschwerden in Patent-, Gebrauchsmuster-, Sortenschutz- und Warenzeichensachen (BPatG)
 WF,Sonstige Beschwerden in Familiensachen (OLG)
-WpSt(B),Beschwerden gegen die Nichtzulassung der Revision und Beschwerden in berufsgerichtlichen Verfahren in Wirtschaftsprüfersachen (BGH, GenBAnw.)
-WpSt(R),Revisionen in berufsgerichtlichen Verfahren in Wirtschaftsprüfersachen (BGH, GenBAnw.)
+WpSt (B),Beschwerden gegen die Nichtzulassung der Revision und Beschwerden in berufsgerichtlichen Verfahren in Wirtschaftsprüfersachen (BGH, GenBAnw.)
+WpSt (R),Revisionen in berufsgerichtlichen Verfahren in Wirtschaftsprüfersachen (BGH, GenBAnw.)
 Ws,Beschwerden in Straf- und Bußgeldsachen (OLG)
 Ww,Beschwerden in Landwirtschaftssachen (OLG)
 Wx,Weitere Beschwerden in freiwilliger Gerichtsbarkeit (OLG)
 ZA,Anträge außerhalb eines in der Rechtsmittelinstanz anhängigen Verfahrens (BGH-Zivilsachen)
-ZA-(Pat),Anträge außerhalb anhängiger Patentsachen (BPatG)
+ZA (pat),Anträge außerhalb anhängiger Patentsachen (BPatG)
 ZB,Beschwerden, Rechtsbeschwerden, weitere Beschwerden, Beschwerden gegen die Nichtzulassung der Revision nach dem BEG (BGH-Zivilsachen)
 ZR,Revisionen, Beschwerden gegen die Nichtzulassung der Revision, Anträge auf Zulassung der Sprungrevision, Berufungen in Patentsachen (BGH-Zivilsachen)
-ZR(Ü),Erstinstanzliche Klagen auf Entschädigung wegen überlanger Gerichtsverfahren und strafrechtlicher Ermittlungsverfahren (BGH-Zivilsachen)
+ZR (Ü),Erstinstanzliche Klagen auf Entschädigung wegen überlanger Gerichtsverfahren und strafrechtlicher Ermittlungsverfahren (BGH-Zivilsachen)
 Zs,Beschwerden gegen Amts- und Staatsanwälte (GenStA)
\ No newline at end of file
diff --git a/refex/errors.py b/refex/errors.py
index 26287d0..794871a 100644
--- a/refex/errors.py
+++ b/refex/errors.py
@@ -1,4 +1,3 @@
-
 class RefExError(ValueError):
     pass
 
diff --git a/refex/extractor.py b/refex/extractor.py
index 0b71610..cada667 100644
--- a/refex/extractor.py
+++ b/refex/extractor.py
@@ -1,6 +1,6 @@
 import logging
 import re
-from typing import List
+from typing import List, Tuple
 
 from refex.errors import RefExError
 from refex.extractors.case import CaseRefExtractorMixin
@@ -10,13 +10,15 @@ from refex.models import RefMarker
 logger = logging.getLogger(__name__)
 
 
-class RefExtractor(DivideAndConquerLawRefExtractorMixin, CaseRefExtractorMixin): # LawRefExtractorMixin
+class RefExtractor(
+    DivideAndConquerLawRefExtractorMixin, CaseRefExtractorMixin
+):  # LawRefExtractorMixin
     """
 
     Reference marker format: [ref=UUID]...[/ref]
 
     """
-		
+
     do_law_refs = True
     do_case_refs = True
 
@@ -30,23 +32,37 @@ class RefExtractor(DivideAndConquerLawRefExtractorMixin, CaseRefExtractorMixin):
         """
         marker_offset = 0
         content_with_markers = content
-        sorted_markers = sorted(reference_markers, key=lambda k: k.get_start_position())  # order by occurrence in text
+        sorted_markers = sorted(
+            reference_markers, key=lambda k: k.get_start_position()
+        )  # order by occurrence in text
 
         for i, marker in enumerate(sorted_markers):
             # Check on overlaps
-            if i > 0 and sorted_markers[i - 1].get_end_position() >= marker.get_start_position():
-                raise RefExError('Marker overlaps with previous marker: %s' % marker)
-            elif i + 1 < len(sorted_markers) and sorted_markers[i + 1].get_start_position() <= marker.get_end_position():
-                raise RefExError('Marker overlaps with next marker: %s' % marker)
+            if (
+                i > 0
+                and sorted_markers[i - 1].get_end_position()
+                >= marker.get_start_position()
+            ):
+                raise RefExError("Marker overlaps with previous marker: %s" % marker)
+            elif (
+                i + 1 < len(sorted_markers)
+                and sorted_markers[i + 1].get_start_position()
+                <= marker.get_end_position()
+            ):
+                raise RefExError("Marker overlaps with next marker: %s" % marker)
             else:
                 # Everything fine, replace content
-                content_with_markers, marker_offset = marker.replace_content(content_with_markers, marker_offset)
+                content_with_markers, marker_offset = marker.replace_content(
+                    content_with_markers, marker_offset
+                )
 
         return content_with_markers
 
-    def extract(self, content_html: str, is_html: bool=False):
+    def extract(
+        self, content_html: str, is_html: bool = False
+    ) -> Tuple[str, List[RefMarker]]:
 
-        reference_markers = []  # type: List[RefMarker]
+        reference_markers: List[RefMarker] = []
 
         # Remove all reference markers (HTML or MarkDown)
         content = self.remove_markers(content_html)
@@ -55,13 +71,13 @@ class RefExtractor(DivideAndConquerLawRefExtractorMixin, CaseRefExtractorMixin):
             markers = self.extract_law_ref_markers(content, is_html)
             reference_markers.extend(markers)
 
-            logger.debug('Extracted law ref markers: %i' % len(markers))
+            logger.debug("Extracted law ref markers: %i" % len(markers))
 
         if self.do_case_refs:
             markers = self.extract_case_ref_markers(content)
             reference_markers.extend(markers)
 
-            logger.debug('Extracted case ref markers: %i' % len(markers))
+            logger.debug("Extracted case ref markers: %i" % len(markers))
 
         # Add markers to content
         content_with_markers = self.replace_content(content, reference_markers)
@@ -69,5 +85,5 @@ class RefExtractor(DivideAndConquerLawRefExtractorMixin, CaseRefExtractorMixin):
         return content_with_markers, reference_markers
 
     @staticmethod
-    def remove_markers(value):
-        return re.sub(r'\[ref=([-a-z0-9]+)\](.*?)\[\/ref\]', r'\2', value)
+    def remove_markers(value: str) -> str:
+        return re.sub(r"\[ref=([-a-z0-9]+)\](.*?)\[\/ref\]", r"\2", value)
diff --git a/refex/extractors/case.py b/refex/extractors/case.py
index 6570342..60f67c3 100644
--- a/refex/extractors/case.py
+++ b/refex/extractors/case.py
@@ -2,7 +2,7 @@ import collections
 import logging
 import os
 import re
-from typing import List, Set, Match
+from typing import List, Set, Match, Optional
 
 from refex.models import RefMarker, Ref, RefType
 
@@ -12,7 +12,7 @@ logger = logging.getLogger(__name__)
 class CaseRefExtractorMixin(object):
     court_context = None
     codes = [
-        'Sa',
+        "Sa",
     ]
 
     def clean_text_for_tokenizer(self, text):
@@ -22,25 +22,29 @@ class CaseRefExtractorMixin(object):
         :param text:
         :return:
         """
+
         def repl(m):
-            return '_' * (len(m.group()))
+            return "_" * (len(m.group()))
 
         def repl2(m):
             # print(m.group(2))
-            return m.group(1) + ('_' * (len(m.group(2)) + 1))
+            return m.group(1) + ("_" * (len(m.group(2)) + 1))
 
         # (...) and [...]
-        text = re.sub(r'\((.*?)\)', repl, text)
+        text = re.sub(r"\((.*?)\)", repl, text)
 
         # Dates
-        text = re.sub(r'(([0-9]+)\.([0-9]+)\.([0-9]+)|i\.S\.d\.)', repl, text)
+        text = re.sub(r"(([0-9]+)\.([0-9]+)\.([0-9]+)|i\.S\.d\.)", repl, text)
 
         # Abbr.
-        text = re.sub(r'(\s|\(|\[)([0-9]+|[IVX]+|[a-zA-Z]|sog|ca|Urt|Abs|Nr|lfd|vgl|Rn|Rspr|std|ff|bzw|Art)\.', repl2, text)
+        text = re.sub(
+            r"(\s|\(|\[)([0-9]+|[IVX]+|[a-zA-Z]|sog|ca|Urt|Abs|Nr|lfd|vgl|Rn|Rspr|std|ff|bzw|Art)\.",
+            repl2,
+            text,
+        )
 
         # Schl.-Holst.
-        text = re.sub(r'([a-z]+)\.-([a-z]+)\.', repl, text, flags=re.IGNORECASE)
-
+        text = re.sub(r"([a-z]+)\.-([a-z]+)\.", repl, text, flags=re.IGNORECASE)
 
         return text
 
@@ -54,53 +58,73 @@ class CaseRefExtractorMixin(object):
         # TODO generate only once
 
         federal_courts = [
-            'Bundesverfassungsgericht', 'BVerfG',
-            'Bundesverwaltungsgericht', 'BVerwG',
-            'Bundesgerichtshof', 'BGH',
-            'Bundesarbeitsgericht', 'BAG',
-            'Bundesfinanzhof', 'BFH',
-            'Bundessozialgericht', 'BSG',
-            'Bundespatentgericht', 'BPatG',
-            'Truppendienstgericht Nord', 'TDG Nord',
-            'Truppendienstgericht Süd', 'TDG Süd',
-            'EUGH', 'Truppendienstgericht S&#252;d',
-            'TDG S&#252;d',
+            "Bundesverfassungsgericht",
+            "BVerfG",
+            "Bundesverwaltungsgericht",
+            "BVerwG",
+            "Bundesgerichtshof",
+            "BGH",
+            "Bundesarbeitsgericht",
+            "BAG",
+            "Bundesfinanzhof",
+            "BFH",
+            "Bundessozialgericht",
+            "BSG",
+            "Bundespatentgericht",
+            "BPatG",
+            "Truppendienstgericht Nord",
+            "TDG Nord",
+            "Truppendienstgericht Süd",
+            "TDG Süd",
+            "EUGH",
+            "Truppendienstgericht S&#252;d",
+            "TDG S&#252;d",
         ]
         states = [
-            'Berlin',
-            'Baden-Württemberg', 'BW', 'Baden-W&#252;rttemberg',
-            'Brandenburg', 'Brandenburgisches',
-            'Bremen',
-            'Hamburg',
-            'Hessen',
-            'Niedersachsen',
-            'Hamburg',
-            'Mecklenburg-Vorpommern',
-            'Nordrhein-Westfalen', 'NRW',
-            'Rheinland-Pfalz',
-            'Saarland',
-            'Sachsen',
-            'Sachsen-Anhalt',
-            'Schleswig-Holstein', 'Schl.-Holst.', 'SH',
-            'Thüringen', 'Th&#252;ringen',
+            "Berlin",
+            "Baden-Württemberg",
+            "BW",
+            "Baden-W&#252;rttemberg",
+            "Brandenburg",
+            "Brandenburgisches",
+            "Bremen",
+            "Hamburg",
+            "Hessen",
+            "Niedersachsen",
+            "Hamburg",
+            "Mecklenburg-Vorpommern",
+            "Nordrhein-Westfalen",
+            "NRW",
+            "Rheinland-Pfalz",
+            "Saarland",
+            "Sachsen",
+            "Sachsen-Anhalt",
+            "Schleswig-Holstein",
+            "Schl.-Holst.",
+            "SH",
+            "Thüringen",
+            "Th&#252;ringen",
         ]
         state_courts = [
-            'OVG',
-            'VGH',
-            'LSG',
+            "OVG",
+            "VGH",
+            "LSG",
         ]
         cities = [
-            'Baden-Baden',
-            'Berlin-Brbg.'
-            'Wedding',
-            'Schleswig',
-            'Koblenz',
+            "Baden-Baden",
+            "Berlin-Brbg." "Wedding",
+            "Schleswig",
+            "Koblenz",
+            "Hamm",
         ]
         city_courts = [
-            'Amtsgericht', 'AG',
-            'Landgericht', 'LG',
-            'Oberlandesgericht', 'OLG',
-            'OVG'
+            "Amtsgericht",
+            "AG",
+            "Landgericht",
+            "LG",
+            "Oberlandesgericht",
+            "OLG",
+            "OVG",
         ]
 
         options = []
@@ -110,16 +134,106 @@ class CaseRefExtractorMixin(object):
 
         for court in state_courts:
             for state in states:
-                options.append(court + ' ' + state)
-                options.append(state + ' ' + court)
+                options.append(court + " " + state)
+                options.append(state + " " + court)
 
         for c in city_courts:
             for s in cities:
-                options.append(c + ' ' + s)
-                options.append(s + ' ' + c)
+                options.append(c + " " + s)
+                options.append(s + " " + c)
         # logger.debug('Court regex: %s' % pattern)
 
-        return r'(?P<court>' + ('|'.join(options)) + ')(\s|\.|;|,|:|\))'
+        return r"(?P<court>" + ("|".join(options)) + ")(\s|\.|;|,|:|\))"
+
+    def infer_court(
+        self, file_number: str, match: re.Match, content: str
+    ) -> Optional[str]:
+        """In some cases it is possible to infer the court from the file number.
+        This is currently only implemented for Sozialgerichtsbarkeit ("SG").
+        """
+        SG_MAPPING = {
+            "B": "Bundessozialgericht",
+            "L": "LSG",
+            "S": "SG",
+        }
+
+        if sg_match := re.match(self.get_sozialgerichtsbarkeit_regex(), file_number):
+            instance = SG_MAPPING[sg_match.group("instance")]
+            court_candidate = self.search_court(match, content)
+            if (
+                court_candidate and instance in court_candidate
+            ):  # we can be sure that the correct court was found
+                return court_candidate
+            return instance
+
+        return None
+
+    def search_court(self, match: re.Match, content: str) -> Optional[str]:
+        """Heuristic search. Not yet very reliably (see error cases in test_case_extractor.py)"""
+
+        court = None
+
+        # Search in surroundings for court names
+        for diff in [100, 200, 500]:
+            # TODO maybe search left first, then to the right
+
+            start = max(0, match.start(0) - diff)
+            end = min(len(content), match.end(0) + diff)
+            surrounding = content[start:end]
+
+            # print('Surroundings: %s'  % content[start:end])
+
+            # File number position in surroundings
+            fn_pos = match.start(0) - start
+            candidates = collections.OrderedDict()
+
+            for court_match in re.finditer(self.get_court_name_regex(), surrounding):
+                candidate_pos = round(
+                    (court_match.start(0) + court_match.end(0)) / 2
+                )  # Position = center
+                candidate_dist = abs(fn_pos - candidate_pos)  # Distance to file number
+
+                # print('-- Candidate: %s / pos: %i / dist: %i' % (court_match.group(0), candidate_pos, candidate_dist))
+
+                if candidate_dist not in candidates:
+                    candidates[candidate_dist] = court_match
+                else:
+                    logger.warning(
+                        "Court candidate with same distance exist already: %s"
+                        % court_match
+                    )
+
+            # Court is the candidate with smallest distance to file number
+            if len(candidates) > 0:
+                court = next(iter(candidates.values())).group("court")
+                # Stop searching if court was found with this range
+                break
+
+        return court
+
+    def get_sozialgerichtsbarkeit_regex(self):
+        """
+        Sozialgerichtsbarkeit cases have a special, more expanded format, e.g.: B 6 KA 45/13 R
+        - instance: Gericht bzw. die Instanz
+        - chamber: Kammber bzw. Senat
+        - subject_area: Sachgebietskennzeichen
+        - number: Laufende Nummer
+        - year: Eingangsjahr
+        - register: Verfahrensregister (optional)
+        """
+        pattern = (
+            r"(?P<instance>(B|L|S))"
+            + r"\s"
+            + r"(?P<chamber>[0-9]{1,2})"
+            + r"\s"
+            + r"(?P<subject_area>(A|AL|AS|AY|BK|BL|EG|KA|KG|KR|KS|LW|P|R|RE|RS|SB|SO|SF|U|ÜG|V|VG|VH|VJ|VK|VS))"
+            + r"\s"
+            + r"(?P<number>[0-9]{1,6})"
+            + r"/"
+            + r"(?P<year>[0-9]{2})"
+            + r"(?P<register>\s(AR|B|BH|C|GS|K|KH|R|RH|S))?"
+        )
+        return pattern
 
     def get_file_number_regex(self):
         """
@@ -128,6 +242,8 @@ class CaseRefExtractorMixin(object):
         1 O 137/15
         Au 5 K 17.31263
 
+        The general way file numbers are structured is:
+
         <chamber> <code> <number> / <year>
 
         - chamber: zuständige Richter bzw. Spruchkörper (arabic or roman numbers)
@@ -136,29 +252,42 @@ class CaseRefExtractorMixin(object):
         - separater: / or ,
         - year: Eingangsjahr (numeric, length = 2)
 
-        Note:
+
+        Sozialgerichtsbarkeit ("SG") cases have a special, more expanded format (e.g.: B 6 KA 45/13 R)
+        - instance: Gericht bzw. die Instanz
+        - chamber: Kammber bzw. Senat
+        - subject_area: Sachgebietskennzeichen
+        - number: Laufende Nummer
+        - year: Eingangsjahr
+        - register: Verfahrensregister (optional)
+
+        However, while the SG file numbers are semantically different (e.g. after the chamber, they contain
+        a subject area instead of the code), they syntactically are being matched by the regular file number
+        regex, except for the instance and (an optional) register part. So in order to include SG file numbers
+        fully, the regex is extended with those two elements.
 
         TODO Special cases
 
         BSG: B 6 KA 45/13 R, S 8 AL 144/12
         Bavaria has a different order - <year>.<number>
         - <chamber> <code> <year>.<number>
-
-
-        :return:
         """
 
         # |' + ('|'.join(self.get_codes())) + ')' \
 
-        pattern = r'(?P<chamber>([0-9]+)[a-z]?|([IVX]+))' \
-            + '\s' \
-            + '(?P<code>[A-Za-z]{1,6})' \
-            + '(\s\(([A-Za-z]{1,6})\))?' \
-            + '(\s([A-Za-z]{1,6}))?' \
-            + '\s' \
-            + '(?P<number>[0-9]{1,6})' \
-            + '\/' \
-            + '(?P<year>[0-9]{2})'
+        pattern = (
+            r"((?P<instance_for_sg>(B|L|S))\s)?"  # only for SG
+            + r"(?P<chamber>([0-9]{1,2})[a-z]?|([IVX]+))"
+            + "\s"
+            + "(?P<code>[A-Z][A-Za-z]{0,4})"
+            + "(\s\(([A-Za-z]{1,6})\))?"
+            + "(\s([A-Za-z]{1,6}))?"
+            + "\s"
+            + "(?P<number>[0-9]{1,6})"
+            + "(\/|\.)"
+            + "(?P<year>[0-9]{2})"
+            + r"(\s(?P<register>(AR|B|BH|C|GS|K|KH|R|RH|S)))?"  # only for SG
+        )
 
         return pattern
 
@@ -216,75 +345,46 @@ class CaseRefExtractorMixin(object):
 
         # Find all file numbers
         for match in re.finditer(self.get_file_number_regex(), content):  # type: Match
-            court = None
-
-            # Search in surroundings for court names
-            for diff in [100, 200, 500]:
-                # TODO maybe search left first, then to the right
-
-                start = max(0, match.start(0) - diff)
-                end = min(len(content), match.end(0) + diff)
-                surrounding = content[start:end]
-
-                # print('Surroundings: %s'  % content[start:end])
-
-                # File number position in surroundings
-                fn_pos = match.start(0) - start
-                candidates = collections.OrderedDict()
-
-                for court_match in re.finditer(self.get_court_name_regex(), surrounding):
-                    candidate_pos = round((court_match.start(0) + court_match.end(0)) / 2)  # Position = center
-                    candidate_dist = abs(fn_pos - candidate_pos)  # Distance to file number
 
-                    # print('-- Candidate: %s / pos: %i / dist: %i' % (court_match.group(0), candidate_pos, candidate_dist))
-
-                    if candidate_dist not in candidates:
-                        candidates[candidate_dist] = court_match
-                    else:
-                        logger.warning('Court candidate with same distance exist already: %s' % court_match)
-
-                # Court is the candidate with smallest distance to file number
-                if len(candidates) > 0:
-                    court = next(iter(candidates.values())).group('court')
-                    # Stop searching if court was found with this range
-                    break
+            file_number = match.group(0)
 
-            if court is None:
-                court = ''
+            court = (
+                self.infer_court(file_number, match, content)
+                or self.search_court(match, content)
+                or ""
+            )
 
             file_number = match.group(0)
             ref_ids = [
-                Ref(ref_type=RefType.CASE, court=court, file_number=file_number)  # TODO date field
+                Ref(
+                    ref_type=RefType.CASE, court=court, file_number=file_number
+                )  # TODO date field
             ]
             # TODO maintain order for case+law refs
-            marker = RefMarker(text=file_number,
-                               start=match.start(0),
-                               end=match.end(0),
-                               line=0)  # TODO line number
+            marker = RefMarker(
+                text=file_number, start=match.start(0), end=match.end(0), line=0
+            )  # TODO line number
             marker.set_uuid()
             marker.set_references(ref_ids)
 
-            refs.append(
-                marker
-            )
+            refs.append(marker)
 
             # print(match.start(0))
 
         return refs
 
-
     def get_codes(self) -> Set[str]:
         """Codes used in file numbers"""
-        data_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'data')
-        code_path = os.path.join(data_dir, 'file_number_codes.csv')
+        data_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), "data")
+        code_path = os.path.join(data_dir, "file_number_codes.csv")
 
-        with open(code_path, 'r') as f:
+        with open(code_path, "r") as f:
             codes = []
             for line in f.readlines():
-                cols = line.strip().split(',', 2)
+                cols = line.strip().split(",", 2)
 
                 # Strip parenthesis
-                code = re.sub(r'\((.*?)\)', '', cols[0])
+                code = re.sub(r"\s\((.*?)\)", "", cols[0])
 
                 codes.append(code)
 
diff --git a/refex/extractors/law.py b/refex/extractors/law.py
index dd344cb..d042a86 100644
--- a/refex/extractors/law.py
+++ b/refex/extractors/law.py
@@ -30,9 +30,25 @@ class LawRefExtractorMixin(object):
     law_book_context = None
 
     # Book identifiers (used to generate regular expression)
-    law_book_codes = []
-    default_law_book_codes = ['AsylG', 'BGB', 'GG', 'VwGO', 'GkG', 'stbstg', 'lbo', 'ZPO', 'LVwG', 'AGVwGO SH', 'BauGB',
-                                'BauNVO', 'ZWStS', 'SbStG', 'StPO', 'TKG']
+    law_book_codes: List[str] = []
+    default_law_book_codes = [
+        "AsylG",
+        "BGB",
+        "GG",
+        "VwGO",
+        "GkG",
+        "stbstg",
+        "lbo",
+        "ZPO",
+        "LVwG",
+        "AGVwGO SH",
+        "BauGB",
+        "BauNVO",
+        "ZWStS",
+        "SbStG",
+        "StPO",
+        "TKG",
+    ]
 
     def extract_law_ref_markers(self, content: str) -> List[RefMarker]:
         """
@@ -43,7 +59,7 @@ class LawRefExtractorMixin(object):
         :return: List of reference markers
         """
 
-        logger.debug('Extracting from: %s' % content)
+        logger.debug("Extracting from: %s" % content)
 
         if self.law_book_context is not None:
             # Extraction with context available is done in another method
@@ -54,25 +70,33 @@ class LawRefExtractorMixin(object):
         marker_offset = 0
 
         # Handle each match separately
-        for marker_match in re.finditer(self.get_law_ref_regex(self.get_law_book_codes()), content):
+        for marker_match in re.finditer(
+            self.get_law_ref_regex(self.get_law_book_codes()), content
+        ):
 
             marker_text = str(marker_match.group(0)).strip()
-            references = []
+            references: List[Ref] = []
 
             # Handle single and multi refs separately
-            if re.match(r'^(Art(\.{,1})|§)\s', marker_text):
-                references = self.handle_single_law_ref(self.get_law_book_codes(), marker_text, references)
+            if re.match(r"^(Art(\.{,1})|§)\s", marker_text):
+                references = self.handle_single_law_ref(
+                    self.get_law_book_codes(), marker_text, references
+                )
 
-            elif re.match(r'^§§\s', marker_text):
-                references = self.handle_multiple_law_refs(self.get_law_book_codes(), marker_text, references)
+            elif re.match(r"^§§\s", marker_text):
+                references = self.handle_multiple_law_refs(
+                    self.get_law_book_codes(), marker_text, references
+                )
 
             else:
-                raise RefExError('Unsupported ref beginning: %s' % marker_text)
-
-            marker = RefMarker(text=marker_text,
-                               start=marker_match.start(),
-                               end=marker_match.end(),
-                               line=0)  # TODO
+                raise RefExError("Unsupported ref beginning: %s" % marker_text)
+
+            marker = RefMarker(
+                text=marker_text,
+                start=marker_match.start(),
+                end=marker_match.end(),
+                line=0,
+            )  # TODO
             marker.set_uuid()
             marker.set_references(references)
 
@@ -91,7 +115,9 @@ class LawRefExtractorMixin(object):
 
         return self.law_book_codes
 
-    def get_law_book_ref_regex(self, law_book_codes, optional=False, group_name=False, to_lower=False):
+    def get_law_book_ref_regex(
+        self, law_book_codes, optional=False, group_name=False, to_lower=False
+    ):
         """
         Returns regex for law book part in reference markers (OR list).
 
@@ -104,17 +130,17 @@ class LawRefExtractorMixin(object):
         # return '[a-zA-Z]'
 
         if len(law_book_codes) < 1:
-            raise RefExError('Cannot generate regex, law_book_codes are empty')
+            raise RefExError("Cannot generate regex, law_book_codes are empty")
 
         if optional:
-            raise ValueError('optional=True not supported')
+            raise ValueError("optional=True not supported")
 
         if group_name:
-            raise ValueError('group_name=True not supported')
+            raise ValueError("group_name=True not supported")
 
-        logger.debug('Law book ref with %i books' % len(law_book_codes))
+        logger.debug("Law book ref with %i books" % len(law_book_codes))
 
-        return '|'.join([code.lower() if to_lower else code for code in law_book_codes])
+        return "|".join([code.lower() if to_lower else code for code in law_book_codes])
 
     def get_law_ref_regex(self, law_book_codes) -> str:
         """
@@ -134,10 +160,21 @@ class LawRefExtractorMixin(object):
         :return: regular expression
         """
 
-        regex = r'(§|§§|Art(\.{,1}))\s'
-        regex += '(\s|[0-9]+(\.{,1})|[a-z]|Abs\.|Abs|Satz|Halbsatz|S\.|Nr|Nr\.|Alt|Alt\.|f\.|ff\.|und|bis|\,|;|\s' \
-                 + self.get_law_book_ref_regex(law_book_codes, optional=False, group_name=False) + ')+'
-        regex += '\s(' + self.get_law_book_ref_regex(law_book_codes, optional=False, group_name=False) + ')'
+        regex = r"(§|§§|Art(\.{,1}))\s"
+        regex += (
+            "(\s|[0-9]+(\.{,1})|[a-z]|Abs\.|Abs|Satz|Halbsatz|S\.|Nr|Nr\.|Alt|Alt\.|f\.|ff\.|und|bis|\,|;|\s"
+            + self.get_law_book_ref_regex(
+                law_book_codes, optional=False, group_name=False
+            )
+            + ")+"
+        )
+        regex += (
+            "\s("
+            + self.get_law_book_ref_regex(
+                law_book_codes, optional=False, group_name=False
+            )
+            + ")"
+        )
 
         # logger.debug('Regex: %s' % regex)
 
@@ -145,49 +182,54 @@ class LawRefExtractorMixin(object):
 
     def get_law_ref_match_single(self, law_book_codes, ref_str):
         # Single ref
-        regex_a = '(Art(\.{,1})|§)\s'
-        regex_a += '((?P<sect>[0-9]+)\s?(?P<sect_az>[a-z])?)'                # f. ff.
-        regex_a += '(\s?([0-9]+(\.{,1})|[a-z]{1,2}|Abs\.|Abs|Satz|Halbsatz|S\.|Nr|Nr\.|Alt|Alt\.|und|bis|,|;|\s))*'
+        regex_a = "(Art(\.{,1})|§)\s"
+        regex_a += "((?P<sect>[0-9]+)\s?(?P<sect_az>[a-z])?)"  # f. ff.
+        regex_a += "(\s?([0-9]+(\.{,1})|[a-z]{1,2}|Abs\.|Abs|Satz|Halbsatz|S\.|Nr|Nr\.|Alt|Alt\.|und|bis|,|;|\s))*"
         # regex_a += '\s?(?:(Abs.|Abs)\s?(?:[0-9]{1,2})\s?((,|und|bis)\s*(([0-9]+)\s?([a-z])?)+)*)?'
         # regex_a += '\s?((Satz|S\.)\s[0-9]{1,2})?'
         # regex_a += '\s?(((Nr|Nr\.)\s[0-9]+)' + '(\s?(,|und|bis)\s[0-9]+)*' + ')?'
         # regex_a += '\s?(((Alt|Alt\.)\s[0-9]+)' + '(\s?(,|und|bis)\s[0-9]+)*' + ')?'
 
-        regex_a += '\s(?P<book>' + self.get_law_book_ref_regex(law_book_codes, optional=False) + ')'
+        regex_a += (
+            "\s(?P<book>"
+            + self.get_law_book_ref_regex(law_book_codes, optional=False)
+            + ")"
+        )
 
         return re.search(regex_a, ref_str)
 
     def get_law_ref_match_multi(self, law_book_codes, ref_str):
-        pattern = r'(?P<delimiter>§§|,|;|und|bis)\s?'
-        pattern += '((?P<sect>[0-9]+)\s?'
+        pattern = r"(?P<delimiter>§§|,|;|und|bis)\s?"
+        pattern += "((?P<sect>[0-9]+)\s?"
         # pattern += '(?P<sect_az>[a-z])?)'
         # (?!.*?bis).*([a-z]).*)
         # pattern += '(?P<sect_az>(?!.*?bis).*([a-z]).*)?)'
         # (?!(moscow|outside))
         # pattern += '(?P<sect_az>(([a-z])(?!(und|bis))))?)'
-        pattern += '((?P<sect_az>[a-z])(\s|,|;))?)'  # Use \s|, to avoid matching "bis" and "Abs", ...
+        pattern += "((?P<sect_az>[a-z])(\s|,|;))?)"  # Use \s|, to avoid matching "bis" and "Abs", ...
 
-        pattern += '(\s?(Abs\.|Abs)\s?([0-9]+))*'
-        pattern += '(\s?(S\.|Satz|Halbsatz)\s?([0-9]+))*'
+        pattern += "(\s?(Abs\.|Abs)\s?([0-9]+))*"
+        pattern += "(\s?(S\.|Satz|Halbsatz)\s?([0-9]+))*"
 
         # pattern += '(?:\s(Nr\.|Nr)\s([0-9]+))'
         # pattern += '(?:\s(S\.|Satz)\s([0-9]+))'
 
         # pattern += '(?:\s(f\.|ff\.))?'
-        pattern += '(\s?(f\.|ff\.))*'
+        pattern += "(\s?(f\.|ff\.))*"
 
         # pattern += '(\s(Abs.|Abs)\s?([0-9]+)((,|und|bis)\s([0-9]+))*)*'
         # pattern += '\s?(?:(Abs.|Abs)\s?(?:[0-9]{1,2})\s?((,|und|bis)\s*(([0-9]+)\s?([a-z])?)+)*)?'
 
         # logger.debug('Multi ref regex: %s' % pattern)
 
-
-        pattern += '\s?(?:(?P<book>' + self.get_law_book_ref_regex(law_book_codes) + '))?'
+        pattern += (
+            "\s?(?:(?P<book>" + self.get_law_book_ref_regex(law_book_codes) + "))?"
+        )
 
         # print('MULTI: ' + ref_str)
         # print(pattern)
         #
-        logger.debug('Multi ref regex: %s' % pattern)
+        logger.debug("Multi ref regex: %s" % pattern)
 
         return re.finditer(pattern, ref_str)
 
@@ -199,41 +241,49 @@ class LawRefExtractorMixin(object):
         prev_sect = None
         prev_book = None
 
-        logger.debug('Multi refs found in: %s' % ref_str)
+        logger.debug("Multi refs found in: %s" % ref_str)
 
         # Loop over all results
         for match in matches:
 
             # If book is not set, use __placeholder__ and replace later
-            if match.group('book') is not None:
-                book = match.group('book').lower()
+            if match.group("book") is not None:
+                book = match.group("book").lower()
             else:
-                book = '__book__'
+                book = "__book__"
 
             # Section must exist
-            if match.group('sect') is not None:
-                sect = str(match.group('sect'))
+            if match.group("sect") is not None:
+                sect = str(match.group("sect"))
             else:
-                raise RefExError('Ref sect is not set')
+                raise RefExError("Ref sect is not set")
 
-            if match.group('sect_az') is not None:
-                sect += match.group('sect_az').lower()
+            if match.group("sect_az") is not None:
+                sect += match.group("sect_az").lower()
 
             ref = Ref(ref_type=RefType.LAW, book=book, section=sect)
 
-            logger.debug('Ref found: %s (%s)' % (ref, match.group(0)))
+            logger.debug("Ref found: %s (%s)" % (ref, match.group(0)))
 
             # Check for section ranges
-            if match.group('delimiter') == 'bis':
-                logger.debug('Handle section range - Add ids from ' + prev_sect + ' to ' + sect)
+            if match.group("delimiter") == "bis":
+                logger.debug(
+                    "Handle section range - Add ids from " + prev_sect + " to " + sect
+                )
                 # TODO how to handle az sects
-                prev_sect = re.sub('[^0-9]', '', prev_sect)
-                sect = re.sub('[^0-9]', '', sect)
+                prev_sect = re.sub("[^0-9]", "", prev_sect)
+                sect = re.sub("[^0-9]", "", sect)
 
-                for between_sect in range(int(prev_sect)+1, int(sect)):
+                for between_sect in range(int(prev_sect) + 1, int(sect)):
                     # print(between_sect)
 
-                    refs_tmp.append(Ref(ref_type=RefType.LAW, book=prev_book, section=str(between_sect)))
+                    refs_tmp.append(
+                        Ref(
+                            ref_type=RefType.LAW,
+                            book=prev_book,
+                            section=str(between_sect),
+                        )
+                    )
             else:
                 prev_sect = sect
                 prev_book = book
@@ -242,12 +292,12 @@ class LawRefExtractorMixin(object):
 
         # law_ids.append('multi = ' + ref_str)
         # handle __book__
-        logger.debug('All law ids found: %s' % refs_tmp)
+        logger.debug("All law ids found: %s" % refs_tmp)
 
         refs_tmp.reverse()
         book = None
         for id_tmp in refs_tmp:
-            if id_tmp.book != '__book__':
+            if id_tmp.book != "__book__":
                 book = id_tmp.book
             elif book is not None:
                 id_tmp.book = book
@@ -259,8 +309,8 @@ class LawRefExtractorMixin(object):
 
         return law_ids
 
-    def handle_single_law_ref(self, law_book_codes, ref_str, law_ids):
-        logger.debug('Single ref found in: %s' % ref_str)
+    def handle_single_law_ref(self, law_book_codes, ref_str, law_ids) -> List[Ref]:
+        logger.debug("Single ref found in: %s" % ref_str)
 
         # Single ref
         mm = self.get_law_ref_match_single(law_book_codes, ref_str)
@@ -269,34 +319,33 @@ class LawRefExtractorMixin(object):
         if mm is not None:
             # mm.groupdict()
 
-            if mm.group('book') is not None:
+            if mm.group("book") is not None:
                 # Found book
-                book = mm.group('book').lower()
+                book = mm.group("book").lower()
             else:
-                raise RefExError('Ref book is not set: %s ' % ref_str)
+                raise RefExError("Ref book is not set: %s " % ref_str)
 
-            if mm.group('sect') is not None:
+            if mm.group("sect") is not None:
                 # Found section
-                sect = str(mm.group('sect'))
+                sect = str(mm.group("sect"))
             else:
-                raise RefExError('Ref sect is not set')
+                raise RefExError("Ref sect is not set")
 
-            if mm.group('sect_az') is not None:
+            if mm.group("sect_az") is not None:
                 # Found section addon
-                sect += mm.group('sect_az').lower()
+                sect += mm.group("sect_az").lower()
 
             law_id = Ref(ref_type=RefType.LAW, book=book, section=sect)
 
-            logger.debug('Law ID: %s' % law_id)
+            logger.debug("Law ID: %s" % law_id)
 
             law_ids.append(law_id)
         else:
             # law_ids.append({'book': 'not matched', 'sect': 'NOT MATCHED (single) %s ' % ref_str})
-            logger.warning('Law ID could not be matched: %s' % ref_str)
+            logger.warning("Law ID could not be matched: %s" % ref_str)
 
         return law_ids
 
-
     def extract_law_ref_markers_with_context(self, content):
         """
         With context = citing law book is known
@@ -308,7 +357,7 @@ class LawRefExtractorMixin(object):
         markers = []
 
         book_code = self.law_book_context
-        content = content.replace('&#167;', '§')
+        content = content.replace("&#167;", "§")
         search_text = str(content)
 
         def multi_sect(match):
@@ -329,28 +378,26 @@ class LawRefExtractorMixin(object):
         patterns = [
             # §§ 664 bis 670
             {
-                'pattern': '§§ ([0-9]+) (bis|und) ([0-9]+)',
-                'book': multi_book,
-                'sect': multi_sect
+                "pattern": "§§ ([0-9]+) (bis|und) ([0-9]+)",
+                "book": multi_book,
+                "sect": multi_sect,
             },
             # Anlage 3
             {
-                'pattern': 'Anlage ([0-9]+)',
-                'book': lambda match: book_code,
-                'sect': lambda match: 'anlage-%i' % int(match.group(1))
+                "pattern": "Anlage ([0-9]+)",
+                "book": lambda match: book_code,
+                "sect": lambda match: "anlage-%i" % int(match.group(1)),
             },
-
             # § 1
             {
-                'pattern': '§ ([0-9]+)(?:\s(Abs\.|Absatz)\s([0-9]+))?(?:\sSatz\s([0-9]+))?',
-                'book': lambda match: book_code,
-                'sect': lambda match: match.group(1)
+                "pattern": "§ ([0-9]+)(?:\s(Abs\.|Absatz)\s([0-9]+))?(?:\sSatz\s([0-9]+))?",
+                "book": lambda match: book_code,
+                "sect": lambda match: match.group(1),
             },
-
         ]
 
         for p in patterns:
-            regex = p['pattern']
+            regex = p["pattern"]
 
             res = re.finditer(regex, search_text)  # flags
 
@@ -359,13 +406,15 @@ class LawRefExtractorMixin(object):
 
                 # Build ref with lambda functions
                 ref_ids = []
-                books = p['book'](ref_m)
-                sects = p['sect'](ref_m)
+                books = p["book"](ref_m)
+                sects = p["sect"](ref_m)
 
                 # Handle multiple ref ids in a single marker
                 if not isinstance(books, str):
                     for key, book in enumerate(books):
-                        ref_ids.append(Ref(ref_type=RefType.LAW, book=book, section=sects[key]))
+                        ref_ids.append(
+                            Ref(ref_type=RefType.LAW, book=book, section=sects[key])
+                        )
 
                 else:
                     ref_ids.append(Ref(ref_type=RefType.LAW, book=books, section=sects))
@@ -376,8 +425,11 @@ class LawRefExtractorMixin(object):
                 markers.append(ref)
 
                 # Remove from search content to avoid duplicate matches
-                search_text = search_text[:ref_m.start()] + ('_' * (ref_m.end() - ref_m.start())) \
-                              + search_text[ref_m.end():]
+                search_text = (
+                    search_text[: ref_m.start()]
+                    + ("_" * (ref_m.end() - ref_m.start()))
+                    + search_text[ref_m.end() :]
+                )
                 # print('-------')
 
         # Sort by start and replace markers
diff --git a/refex/extractors/law_dnc.py b/refex/extractors/law_dnc.py
index 04335dd..7a23dfe 100644
--- a/refex/extractors/law_dnc.py
+++ b/refex/extractors/law_dnc.py
@@ -30,14 +30,35 @@ class DivideAndConquerLawRefExtractorMixin(object):
     law_book_context = None
 
     # Book identifiers (used to generate regular expression)
-    law_book_codes = []
-    default_law_book_codes = ['AsylG', 'BGB', 'GG', 'VwGO', 'GkG', 'stbstg', 'lbo', 'ZPO', 'LVwG', 'AGVwGO SH', 'BauGB',
-                                'BauNVO', 'ZWStS', 'SbStG', 'StPO', 'TKG', 'SG', 'SGG', 'SGB X']
+    law_book_codes: List[str] = []
+    default_law_book_codes = [
+        "AsylG",
+        "BGB",
+        "GG",
+        "VwGO",
+        "GkG",
+        "stbstg",
+        "lbo",
+        "ZPO",
+        "LVwG",
+        "AGVwGO SH",
+        "BauGB",
+        "BauNVO",
+        "ZWStS",
+        "SbStG",
+        "StPO",
+        "TKG",
+        "SG",
+        "SGG",
+        "SGB X",
+    ]
 
     # All text non-word symbols
-    word_delimiter = '\s|\.|,|;|:|!|\?|\(|\)|\[|\]|"|\'|<|>|&'
+    word_delimiter = "\s|\.|,|;|:|!|\?|\(|\)|\[|\]|\"|'|<|>|&"
 
-    def extract_law_ref_markers(self, content: str, is_html: bool=False) -> List[RefMarker]:
+    def extract_law_ref_markers(
+        self, content: str, is_html: bool = False
+    ) -> List[RefMarker]:
         """
 
         The main extraction method. Takes input content and returns content with markers and list of extracted references.
@@ -59,38 +80,43 @@ class DivideAndConquerLawRefExtractorMixin(object):
 
         # Replace special characters if working with html
         if is_html:
-            sectionSign = '&#167;'
-            self.word_delimiter = '\s|\.|,|;|:|!|\?|\(|\)|\[|\]|&#8221;|\&#8216;|\&#8217;|&#60;|&#62;|&#38;|&rdquo;|\&lsquo;|\&rsquo;|&lt;|&gt;|&amp;|"|\'|<|>|&'
+            sectionSign = "&#167;"
+            self.word_delimiter = "\s|\.|,|;|:|!|\?|\(|\)|\[|\]|&#8221;|\&#8216;|\&#8217;|&#60;|&#62;|&#38;|&rdquo;|\&lsquo;|\&rsquo;|&lt;|&gt;|&amp;|\"|'|<|>|&"
         else:
-            sectionSign = '§'
-            self.word_delimiter = '\s|\.|,|;|:|!|\?|\(|\)|\[|\]|"|\'|<|>|&'
+            sectionSign = "§"
+            self.word_delimiter = "\s|\.|,|;|:|!|\?|\(|\)|\[|\]|\"|'|<|>|&"
 
-
-
-        book_look_ahead = '(?=' + self.word_delimiter + ')'  # book code should be followed by a word separator, e.g. space.
+        book_look_ahead = (
+            "(?=" + self.word_delimiter + ")"
+        )  # book code should be followed by a word separator, e.g. space.
 
         # Single ref
         book_pattern = self.get_law_book_ref_regex(self.get_law_book_codes())
 
-
-
         # Any content
-        any_content = '(\s?([0-9]{1,5}(\.{,1})|[a-z]{1,2}|[IXV]{1,3}|Abs\.|Abs|Satz|Halbsatz|S\.|Nr|Nr\.|Alt|Alt\.|und|bis|,|;|\s))*'
-        any_content = '([0-9]{1,5}|\.|[a-z]|[IXV]{1,3}|Abs\.|Abs|Satz|Halbsatz|S\.|Nr|Nr\.|Alt|Alt\.|und|bis|,|;|\s)*'
-
-
-        multi_pattern = sectionSign + sectionSign + ' (\s|[0-9]+(\.{,1})|[a-z]|Abs\.|Abs|Satz|Halbsatz|S\.|Nr|Nr\.|Alt|Alt\.|f\.|ff\.|und|bis|\,|;|\s'+ book_pattern + ')+\s(' + book_pattern + ')' + book_look_ahead
-
-
-
-
-        for marker_match in re.finditer(re.compile(multi_pattern), content):  # All matches
+        any_content = "(\s?([0-9]{1,5}(\.{,1})|[a-z]{1,2}|[IXV]{1,3}|Abs\.|Abs|Satz|Halbsatz|S\.|Nr|Nr\.|Alt|Alt\.|und|bis|,|;|\s))*"
+        any_content = "([0-9]{1,5}|\.|[a-z]|[IXV]{1,3}|Abs\.|Abs|Satz|Halbsatz|S\.|Nr|Nr\.|Alt|Alt\.|und|bis|,|;|\s)*"
+
+        multi_pattern = (
+            sectionSign
+            + sectionSign
+            + " (\s|[0-9]+(\.{,1})|[a-z]|Abs\.|Abs|Satz|Halbsatz|S\.|Nr|Nr\.|Alt|Alt\.|f\.|ff\.|und|bis|\,|;|\s"
+            + book_pattern
+            + ")+\s("
+            + book_pattern
+            + ")"
+            + book_look_ahead
+        )
+
+        for marker_match in re.finditer(
+            re.compile(multi_pattern), content
+        ):  # All matches
             marker_text = marker_match.group(0)
-            refs = []
+            refs: List[Ref] = []
             refs_waiting_for_book = []
 
             # print('>> ' + marker_text)
-            logger.debug('Multi Match with: %s' % marker_text)
+            logger.debug("Multi Match with: %s" % marker_text)
 
             # Books by position in text
             book_positions = {}  # Can we ensure that book_position is in order?
@@ -99,23 +125,33 @@ class DivideAndConquerLawRefExtractorMixin(object):
 
             # We cannot work without knowing the book
             if len(book_positions) < 0:
-                logger.error('No book found in marker text: %s' % marker_text)
+                logger.error("No book found in marker text: %s" % marker_text)
                 continue
 
             # Extract references from marker text
             # - find for <separator §§|,|..> + <section>
             # - ignore Abs, Nr, ...
             # - corresponding book is the closest to right
-            a = '([0-9]+)\s(?=bis|und)'
-            b = '([0-9]+)\s?[a-z]'
-            c = '([0-9]+)'
-            pattern = '(?P<sep>' + sectionSign + sectionSign + '|,|;|und|bis)\s?(?P<sect>(' + a + '|' + b + '|' + c + '))'
-
+            a = "([0-9]+)\s(?=bis|und)"
+            b = "([0-9]+)\s?[a-z]"
+            c = "([0-9]+)"
+            pattern = (
+                "(?P<sep>"
+                + sectionSign
+                + sectionSign
+                + "|,|;|und|bis)\s?(?P<sect>("
+                + a
+                + "|"
+                + b
+                + "|"
+                + c
+                + "))"
+            )
 
             for ref_match in re.finditer(re.compile(pattern), marker_text):
-                sect = ref_match.group('sect')
+                sect = ref_match.group("sect")
 
-                logger.debug('Found ref: %s' % ref_match.group())
+                logger.debug("Found ref: %s" % ref_match.group())
 
                 if len(book_positions) == 1:
                     book = next(iter(book_positions.values()))
@@ -130,25 +166,30 @@ class DivideAndConquerLawRefExtractorMixin(object):
                             break
 
                 if book is None:
-                    logger.error('No book after reference found: %s - %s' % (ref_match.group(0), marker_text))
+                    logger.error(
+                        "No book after reference found: %s - %s"
+                        % (ref_match.group(0), marker_text)
+                    )
                     continue
 
                 # Check for 'between' (range sections)
-                if ref_match.group('sep') == 'bis' and len(refs) > 0:
+                if ref_match.group("sep") == "bis" and len(refs) > 0:
                     from_sect = refs[-1].section  # last section
 
                     # Both sections should be integers (no a-z sections)
                     if sect.isdigit() and from_sect.isdigit():
                         for between_sect in range(int(from_sect) + 1, int(sect)):
                             # Add to queue
-                            refs.append(Ref.init_law(book=book, section=str(between_sect)))
+                            refs.append(
+                                Ref.init_law(book=book, section=str(between_sect))
+                            )
 
                 refs.append(Ref.init_law(book=book, section=sect))
 
             # Prepare marker
-            marker = RefMarker(text=marker_text,
-                               start=marker_match.start(),
-                               end=marker_match.end())
+            marker = RefMarker(
+                text=marker_text, start=marker_match.start(), end=marker_match.end()
+            )
             marker.set_uuid()
             marker.set_references(refs)
 
@@ -159,20 +200,40 @@ class DivideAndConquerLawRefExtractorMixin(object):
                 # Update content to avoid double matching
                 content = marker.replace_content_with_mask(content)
             else:
-                logger.warning('No references found in marker: %s ' % marker_text)
-
+                logger.warning("No references found in marker: %s " % marker_text)
 
         # Single refs
-        sect_pattern = '(?P<sect>([0-9]+)(\s?[a-z]?))'
+        sect_pattern = "(?P<sect>([0-9]+)(\s?[a-z]?))"
         patterns = [
             # § 3 BGB, § 3d BGB, § 83 d BGB
-            sectionSign + ' ' + sect_pattern + ' (?P<book>' + book_pattern + ')' + book_look_ahead,
+            sectionSign
+            + " "
+            + sect_pattern
+            + " (?P<book>"
+            + book_pattern
+            + ")"
+            + book_look_ahead,
             # Abs OR Nr
             # § 42 Abs. 1 Alt. 1 VwGO
-            sectionSign + ' ' + sect_pattern + ' Abs. ([0-9]+) Alt. ([0-9]+) (?P<book>' + book_pattern + ')' + book_look_ahead,
-            sectionSign + ' (?P<sect>([0-9]+)(\s?[a-z]?)) ' + any_content + ' (?P<book>(' + book_pattern + '))' + book_look_ahead,
-            sectionSign + ' (?P<sect>([0-9]+)(\s?[a-z]?)) ' + any_content + ' (?P<next_book>(i\.V\.m\.|iVm))' + book_look_ahead,
-
+            sectionSign
+            + " "
+            + sect_pattern
+            + " Abs. ([0-9]+) Alt. ([0-9]+) (?P<book>"
+            + book_pattern
+            + ")"
+            + book_look_ahead,
+            sectionSign
+            + " (?P<sect>([0-9]+)(\s?[a-z]?)) "
+            + any_content
+            + " (?P<book>("
+            + book_pattern
+            + "))"
+            + book_look_ahead,
+            sectionSign
+            + " (?P<sect>([0-9]+)(\s?[a-z]?)) "
+            + any_content
+            + " (?P<next_book>(i\.V\.m\.|iVm))"
+            + book_look_ahead,
         ]
 
         markers_waiting_for_book = []  # type: List[RefMarker]
@@ -181,18 +242,20 @@ class DivideAndConquerLawRefExtractorMixin(object):
 
             # logger.debug('Pattern: %s' % pattern)
 
-            for marker_match in re.finditer(re.compile(pattern), content):  # All matches
+            for marker_match in re.finditer(
+                re.compile(pattern), content
+            ):  # All matches
                 marker_text = marker_match.group(0)
-                if 'book' in marker_match.groupdict():
-                    book = Ref.clean_book(marker_match.group('book'))
+                if "book" in marker_match.groupdict():
+                    book = Ref.clean_book(marker_match.group("book"))
                 else:
                     book = None
 
-                ref = Ref.init_law(section=marker_match.group('sect'), book=None)
+                ref = Ref.init_law(section=marker_match.group("sect"), book=None)
 
-                marker = RefMarker(text=marker_text,
-                                   start=marker_match.start(),
-                                   end=marker_match.end())
+                marker = RefMarker(
+                    text=marker_text, start=marker_match.start(), end=marker_match.end()
+                )
                 marker.set_uuid()
                 # marker.uuid = 's'
 
@@ -217,14 +280,16 @@ class DivideAndConquerLawRefExtractorMixin(object):
                             markers.append(waiting)
                     markers_waiting_for_book = []
                 else:
-                    if marker_match.group('next_book') is not None:
+                    if marker_match.group("next_book") is not None:
                         marker.set_references([ref])
                         markers_waiting_for_book.append(marker)
                     else:
-                        raise RefExError('next_book and book are None')
+                        raise RefExError("next_book and book are None")
 
         if len(markers_waiting_for_book) > 0:
-            logger.warning('Marker could not be assign to book: %s' % markers_waiting_for_book)
+            logger.warning(
+                "Marker could not be assign to book: %s" % markers_waiting_for_book
+            )
 
         # TODO Art GG
 
@@ -241,7 +306,9 @@ class DivideAndConquerLawRefExtractorMixin(object):
 
         return self.law_book_codes
 
-    def get_law_book_ref_regex(self, law_book_codes, optional=False, group_name=False, to_lower=False):
+    def get_law_book_ref_regex(
+        self, law_book_codes, optional=False, group_name=False, to_lower=False
+    ):
         """
         Returns regex for law book part in reference markers (OR list).
 
@@ -261,15 +328,15 @@ class DivideAndConquerLawRefExtractorMixin(object):
         # return '[a-zA-Z]'
 
         if len(law_book_codes) < 1:
-            raise RefExError('Cannot generate regex, law_book_codes are empty')
+            raise RefExError("Cannot generate regex, law_book_codes are empty")
 
         if optional:
-            raise ValueError('optional=True not supported')
+            raise ValueError("optional=True not supported")
 
         if group_name:
-            raise ValueError('group_name=True not supported')
+            raise ValueError("group_name=True not supported")
 
-        logger.debug('Law book ref with %i books' % len(law_book_codes))
+        logger.debug("Law book ref with %i books" % len(law_book_codes))
 
         # return '|'.join([code.lower() if to_lower else code for code in law_book_codes])
 
@@ -278,7 +345,7 @@ class DivideAndConquerLawRefExtractorMixin(object):
         # optional, max length chars
         # ends with V,G,O or B
         # optional space + roman numbers (e.g. SGB IX)
-        return '([A-ZÄÜÖ][-ÄÜÖäüöA-Za-z]{,20})(V|G|O|B)(?:\s([XIV]{1,5}))?'
+        return "([A-ZÄÜÖ][-ÄÜÖäüöA-Za-z]{,20})(V|G|O|B)(?:\s([XIV]{1,5}))?"
 
     def extract_law_ref_markers_with_context(self, content):
         """
@@ -312,28 +379,26 @@ class DivideAndConquerLawRefExtractorMixin(object):
         patterns = [
             # §§ 664 bis 670
             {
-                'pattern': '§§ ([0-9]+) (bis|und) ([0-9]+)',
-                'book': multi_book,
-                'sect': multi_sect
+                "pattern": "§§ ([0-9]+) (bis|und) ([0-9]+)",
+                "book": multi_book,
+                "sect": multi_sect,
             },
             # Anlage 3
             {
-                'pattern': 'Anlage ([0-9]+)',
-                'book': lambda match: book_code,
-                'sect': lambda match: 'anlage-%i' % int(match.group(1))
+                "pattern": "Anlage ([0-9]+)",
+                "book": lambda match: book_code,
+                "sect": lambda match: "anlage-%i" % int(match.group(1)),
             },
-
             # § 1
             {
-                'pattern': '§ ([0-9]+)(?:\s(Abs\.|Absatz)\s([0-9]+))?(?:\sSatz\s([0-9]+))?',
-                'book': lambda match: book_code,
-                'sect': lambda match: match.group(1)
+                "pattern": "§ ([0-9]+)(?:\s(Abs\.|Absatz)\s([0-9]+))?(?:\sSatz\s([0-9]+))?",
+                "book": lambda match: book_code,
+                "sect": lambda match: match.group(1),
             },
-
         ]
 
         for p in patterns:
-            regex = p['pattern']
+            regex = p["pattern"]
 
             res = re.finditer(regex, search_text)  # flags
 
@@ -342,13 +407,15 @@ class DivideAndConquerLawRefExtractorMixin(object):
 
                 # Build ref with lambda functions
                 ref_ids = []
-                books = p['book'](ref_m)
-                sects = p['sect'](ref_m)
+                books = p["book"](ref_m)
+                sects = p["sect"](ref_m)
 
                 # Handle multiple ref ids in a single marker
                 if not isinstance(books, str):
                     for key, book in enumerate(books):
-                        ref_ids.append(Ref(ref_type=RefType.LAW, book=book, section=sects[key]))
+                        ref_ids.append(
+                            Ref(ref_type=RefType.LAW, book=book, section=sects[key])
+                        )
 
                 else:
                     ref_ids.append(Ref(ref_type=RefType.LAW, book=books, section=sects))
@@ -359,8 +426,11 @@ class DivideAndConquerLawRefExtractorMixin(object):
                 markers.append(ref)
 
                 # Remove from search content to avoid duplicate matches
-                search_text = search_text[:ref_m.start()] + ('_' * (ref_m.end() - ref_m.start())) \
-                              + search_text[ref_m.end():]
+                search_text = (
+                    search_text[: ref_m.start()]
+                    + ("_" * (ref_m.end() - ref_m.start()))
+                    + search_text[ref_m.end() :]
+                )
                 # print('-------')
 
         # Sort by start and replace markers
diff --git a/refex/models.py b/refex/models.py
index 5a79433..ca12ede 100644
--- a/refex/models.py
+++ b/refex/models.py
@@ -2,7 +2,7 @@ import logging
 import uuid
 from enum import Enum
 from functools import total_ordering
-from typing import List, Tuple
+from typing import List, Tuple, Optional
 
 from refex import MARKER_OPEN_FORMAT, MARKER_CLOSE_FORMAT
 
@@ -11,52 +11,56 @@ logger = logging.getLogger(__name__)
 
 class RefType(Enum):
     """Type of referenced document"""
-    CASE = 'case'
-    LAW = 'law'
+
+    CASE = "case"
+    LAW = "law"
 
 
 class BaseRef(object):
-    ref_type = None  # type: RefType
+    ref_type: Optional[RefType] = None
 
     def __init__(self, **kwargs):
-
         self.__dict__.update(kwargs)
 
-
     def __hash__(self):
         return hash(self.__repr__())
 
 
 class CaseRefMixin(BaseRef):
-    file_number = ''
-    ecli = ''
-    court = ''
-    date = ''
+    file_number: str = ""
+    ecli: str = ""
+    court: str = ""
+    date: str = ""
+
+    def get_case_repr(self) -> str:
+        return "%s/%s/%s" % (self.court, self.file_number, self.date)
 
-    def get_case_repr(self):
-        return '%s/%s/%s' % (self.court, self.file_number, self.date)
 
 class LawRefMixin(BaseRef):
-    book = ''  # type: str
-    section = ''  # type: str
-    sentence = '' # type: str
+    book: str = ""
+    section: str = ""
+    sentence: str = ""
 
     @staticmethod
     def init_law(book, section):
-        return Ref(ref_type=RefType.LAW, book=LawRefMixin.clean_book(book), section=LawRefMixin.clean_section(section))
+        return Ref(
+            ref_type=RefType.LAW,
+            book=LawRefMixin.clean_book(book),
+            section=LawRefMixin.clean_section(section),
+        )
 
     @staticmethod
-    def clean_book(book):
+    def clean_book(book: Optional[str]) -> Optional[str]:
         if book is None:
             return None
         return book.strip().lower()
 
     @staticmethod
-    def clean_section(sect):
-        return sect.replace(' ', '').lower()
+    def clean_section(sect: str) -> str:
+        return sect.replace(" ", "").lower()
 
     def get_law_repr(self):
-        return '%s/%s' % (self.book, self.section)
+        return "%s/%s" % (self.book, self.section)
 
 
 @total_ordering
@@ -69,23 +73,33 @@ class Ref(LawRefMixin, CaseRefMixin, BaseRef):
     def __lt__(self, other):
         assert isinstance(other, Ref)
         # return self.__repr__() < other.__repr__()
-        return (self.ref_type.value, self.book, self.section, self.court, self.file_number) < \
-               (other.ref_type.value, other.book, other.section, other.court, other.file_number)
+        return (
+            self.ref_type.value,
+            self.book,
+            self.section,
+            self.court,
+            self.file_number,
+        ) < (
+            other.ref_type.value,
+            other.book,
+            other.section,
+            other.court,
+            other.file_number,
+        )
 
     def __eq__(self, other):
-        assert isinstance(other, Ref) # assumption for this example
+        assert isinstance(other, Ref)  # assumption for this example
         return self.__dict__ == other.__dict__
 
-
     def __repr__(self):
         if self.ref_type == RefType.LAW:
             data = self.get_law_repr()
         elif self.ref_type == RefType.CASE:
             data = self.get_case_repr()
         else:
-            raise ValueError('Unsupported ref type: %s' % self.ref_type)
+            raise ValueError("Unsupported ref type: %s" % self.ref_type)
 
-        return '<Ref(%s: %s)>' % (self.ref_type.value, data)
+        return "<Ref(%s: %s)>" % (self.ref_type.value, data)
         # return 'Ref<%s>' % self.__dict__
         # return 'Ref<%s>' % sorted(self.__dict__.items(), key=lambda x: x[0])
 
@@ -99,18 +113,19 @@ class RefMarker(object):
     have the corresponding source object (LawReferenceMarker: referenced_by = a law object).
 
     """
-    text = ''  # Text of marker
-    uuid = ''
-    start = 0
-    end = 0
-    line = ''  # Line cannot be used with HTML content
-    references = []  # type: List<Ref>
+
+    text: str = ""  # Text of marker
+    uuid: str = ""
+    start: int = 0
+    end: int = 0
+    line: str = ""  # Line cannot be used with HTML content
+    references: List[Ref] = []
 
     # Set by django
     referenced_by = None
     referenced_by_type = None
 
-    def __init__(self, text: str, start: int, end: int, line=''):
+    def __init__(self, text: str, start: int, end: int, line=""):
         self.text = text
         self.start = start
         self.end = end
@@ -130,18 +145,16 @@ class RefMarker(object):
 
         # double replacements
         # alternative: content[start:end]
-        content = content[:start] \
-                  + marker_open \
-                  + self.text \
-                  + marker_close \
-                  + content[end:]
+        content = (
+            content[:start] + marker_open + self.text + marker_close + content[end:]
+        )
 
         return content, marker_offset
 
     def replace_content_with_mask(self, content):
-        mask = '_' * self.get_length() # length of marker
+        mask = "_" * self.get_length()  # length of marker
 
-        return content[:self.start] + mask + content[self.end:]
+        return content[: self.start] + mask + content[self.end :]
 
     def set_uuid(self):
         self.uuid = uuid.uuid4()
@@ -162,4 +175,4 @@ class RefMarker(object):
         return self.end - self.start
 
     def __repr__(self):
-        return '<RefMarker(%s)>' % self.__dict__
+        return "<RefMarker(%s)>" % self.__dict__


Test patch:
diff --git a/refex/tests/__init__.py b/refex/tests/__init__.py
index 9c12056..a9e5cfc 100644
--- a/refex/tests/__init__.py
+++ b/refex/tests/__init__.py
@@ -7,38 +7,41 @@ from refex.models import RefMarker
 
 logger = logging.getLogger(__name__)
 
-logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
+logging.basicConfig(
+    level=logging.DEBUG, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
+)
 
 
 class BaseRefExTest(TestCase):
-    resource_dir = os.path.join(os.path.dirname(__file__), 'resources')
+    resource_dir = os.path.join(os.path.dirname(__file__), "resources")
 
     extractor = RefExtractor()
 
-    def get_book_codes_from_file(self, file_name='law_book_codes.txt'):
+    def get_book_codes_from_file(self, file_name="law_book_codes.txt"):
         app_dir = os.path.dirname(os.path.dirname(__file__))
 
-        with open(os.path.join(app_dir, 'data', file_name)) as f:
+        with open(os.path.join(app_dir, "data", file_name)) as f:
             return [line.strip() for line in f.readlines()]
 
-
-    def assert_refs(self, fixtures, is_html: bool=False):
+    def assert_refs(self, fixtures, is_html: bool = False):
         for i, test in enumerate(fixtures):
-            if 'resource' in test and 'content' not in test:
-                with open(os.path.join(self.resource_dir, test['resource'])) as f:
-                    test['content'] = ''.join(f.readlines())
+            if "resource" in test and "content" not in test:
+                with open(os.path.join(self.resource_dir, test["resource"])) as f:
+                    test["content"] = "".join(f.readlines())
 
-            new_content, markers = self.extractor.extract(test['content'], is_html)
+            new_content, markers = self.extractor.extract(test["content"], is_html)
 
             ref_ids = []
             for ref in markers:  # type: RefMarker
                 ref_ids.extend(ref.get_references())
 
             # print('-----')
-            test['refs'] = sorted(test['refs'])
+            test["refs"] = sorted(test["refs"])
             ref_ids = sorted(ref_ids)
 
-            logger.debug('actual (%i):   %s' % (len(ref_ids), ref_ids))
-            logger.debug('expected (%i): %s' % (len(test['refs']), test['refs']))
+            logger.debug("actual (%i):   %s" % (len(ref_ids), ref_ids))
+            logger.debug("expected (%i): %s" % (len(test["refs"]), test["refs"]))
 
-            self.assertListEqual(ref_ids, test['refs'], 'Invalid ids returned (test #%i)' % i)
+            self.assertListEqual(
+                ref_ids, test["refs"], "Invalid ids returned (test #%i)" % i
+            )
diff --git a/refex/tests/test_case_extractor.py b/refex/tests/test_case_extractor.py
index c546b0b..e8775e5 100644
--- a/refex/tests/test_case_extractor.py
+++ b/refex/tests/test_case_extractor.py
@@ -1,7 +1,6 @@
 import logging
 import os
 import re
-from unittest import skip
 
 from refex.models import RefType, Ref
 from refex.tests import BaseRefExTest
@@ -10,7 +9,6 @@ logger = logging.getLogger(__name__)
 
 
 class CaseRefExTest(BaseRefExTest):
-
     def setUp(self):
         self.extractor.do_law_refs = False
         self.extractor.do_case_refs = True
@@ -18,15 +16,21 @@ class CaseRefExTest(BaseRefExTest):
     def tearDown(self):
         pass
 
-
     def test_regex_file_numbers(self):
-        text = '(vgl. Schl.-Holst. OVG, Urteil vom 19.01.2012 - 1 LB 11/11 -, juris [Rn. 23 f.])'
-        text += '(vgl. OVG NRW, Urteil vom 10.10.1996 - 7 A 4185/95 -, juris [Rn. 68])'
-        text += '(vgl. BVerwG, Beschluss vom 12.11.1987 - 4 B 216/87 -, juris [Rn. 2]; VGH BW, Urteil vom 10.01.2007 - 3 S 1251/06 -, juris [Rn. 25])'
-        text += '(so BVerfG in std. Rspr., vgl. z.B. BVerfG, Beschluss vom 23.07.2003 –- 2 BvR 624/01 -, juris [Rn. 16 f.])'
-        text += ' (vgl. OVG Berlin-Brbg., Urt. v. 17.07.2014 - OVG 7 B 40.13 -, Juris Rn. 35;)'  # TODO file number format
-
-        expected = ['1 LB 11/11', '7 A 4185/95', '4 B 216/87', '3 S 1251/06', '2 BvR 624/01']
+        text = "(vgl. Schl.-Holst. OVG, Urteil vom 19.01.2012 - 1 LB 11/11 -, juris [Rn. 23 f.])"
+        text += "(vgl. OVG NRW, Urteil vom 10.10.1996 - 7 A 4185/95 -, juris [Rn. 68])"
+        text += "(vgl. BVerwG, Beschluss vom 12.11.1987 - 4 B 216/87 -, juris [Rn. 2]; VGH BW, Urteil vom 10.01.2007 - 3 S 1251/06 -, juris [Rn. 25])"
+        text += "(so BVerfG in std. Rspr., vgl. z.B. BVerfG, Beschluss vom 23.07.2003 –- 2 BvR 624/01 -, juris [Rn. 16 f.])"
+        text += " (vgl. OVG Berlin-Brbg., Urt. v. 17.07.2014 - OVG 7 B 40.13 -, Juris Rn. 35;)"
+
+        expected = [
+            "1 LB 11/11",
+            "7 A 4185/95",
+            "4 B 216/87",
+            "3 S 1251/06",
+            "2 BvR 624/01",
+            "7 B 40.13",
+        ]
         actual = []
 
         fns_matches = list(re.finditer(self.extractor.get_file_number_regex(), text))
@@ -34,61 +38,74 @@ class CaseRefExTest(BaseRefExTest):
         for f in fns_matches:
             actual.append(f.group())
 
-        self.assertListEqual(expected, actual, 'Invalid file numbers extracted')
+        self.assertListEqual(expected, actual, "Invalid file numbers extracted")
 
     def test_regex_court_names(self):
-        text = '(vgl. Schl.-Holst. OVG, Urteil vom 19.01.2012 - 1 LB 11/11 -, juris [Rn. 23 f.])'
-        text += '(vgl. OVG NRW, Urteil vom 10.10.1996 - 7 A 4185/95 -, juris [Rn. 68])'
-        text += '(vgl. BVerwG, Beschluss vom 12.11.1987 - 4 B 216/87 -, juris [Rn. 2]; VGH BW, Urteil vom 10.01.2007 - 3 S 1251/06 -, juris [Rn. 25])'
-        text += '(so BVerfG in std. Rspr., vgl. z.B. BVerfG, Beschluss vom 23.07.2003 –- 2 BvR 624/01 -, juris [Rn. 16 f.])'
-
-        expected = ['Schl.-Holst. OVG', 'OVG NRW', 'BVerwG', 'VGH BW', 'BVerfG', 'BVerfG']
+        text = "(vgl. Schl.-Holst. OVG, Urteil vom 19.01.2012 - 1 LB 11/11 -, juris [Rn. 23 f.])"
+        text += "(vgl. OVG NRW, Urteil vom 10.10.1996 - 7 A 4185/95 -, juris [Rn. 68])"
+        text += "(vgl. BVerwG, Beschluss vom 12.11.1987 - 4 B 216/87 -, juris [Rn. 2]; VGH BW, Urteil vom 10.01.2007 - 3 S 1251/06 -, juris [Rn. 25])"
+        text += "(so BVerfG in std. Rspr., vgl. z.B. BVerfG, Beschluss vom 23.07.2003 –- 2 BvR 624/01 -, juris [Rn. 16 f.])"
+
+        expected = [
+            "Schl.-Holst. OVG",
+            "OVG NRW",
+            "BVerwG",
+            "VGH BW",
+            "BVerfG",
+            "BVerfG",
+        ]
         actual = []
 
         fns_matches = list(re.finditer(self.extractor.get_court_name_regex(), text))
 
         for f in fns_matches:
-            actual.append(f.group('court'))
+            actual.append(f.group("court"))
 
-        logger.debug('Actual:   %s' % actual)
-        logger.debug('Expected: %s' % expected)
-
-        self.assertListEqual(sorted(actual), sorted(expected), 'Invalid court names extracted')
+        logger.debug("Actual:   %s" % actual)
+        logger.debug("Expected: %s" % expected)
 
+        self.assertListEqual(
+            sorted(actual), sorted(expected), "Invalid court names extracted"
+        )
 
     def test_clean_text(self):
-        text = 'Obgleich die Baulast ein Institut des in die Kompetenz des Landesgesetzgebers fallenden bauaufsic' \
-               'htlichen Verfahrens ist, der deshalb auch die formellen und materiellen Voraussetzungen ihres Ent' \
-               'stehens und Erlöschens bestimmt, darf sich die übernommene Belastung auch auf die Nutzung des Gru' \
-               'ndstücks in bodenrechtlicher (bebauungsrechtlicher) Hinsicht beziehen (vgl. BVerwG, Beschluss vom' \
-               ' 12.11.1987 - 4 B 216/87 -, juris [Rn. 2]; VGH BW, Urteil vom 10.01.2007 - 3 S 1251/06 -, juris' \
-               ' [Rn. 25]).'
+        text = (
+            "Obgleich die Baulast ein Institut des in die Kompetenz des Landesgesetzgebers fallenden bauaufsic"
+            "htlichen Verfahrens ist, der deshalb auch die formellen und materiellen Voraussetzungen ihres Ent"
+            "stehens und Erlöschens bestimmt, darf sich die übernommene Belastung auch auf die Nutzung des Gru"
+            "ndstücks in bodenrechtlicher (bebauungsrechtlicher) Hinsicht beziehen (vgl. BVerwG, Beschluss vom"
+            " 12.11.1987 - 4 B 216/87 -, juris [Rn. 2]; VGH BW, Urteil vom 10.01.2007 - 3 S 1251/06 -, juris"
+            " [Rn. 25])."
+        )
 
         # print('BEFORE = %s' % text)
         # print()
         # print(self.indexer.clean_text_for_tokenizer(text))
         #
-        self.assertEqual('Obgleich die Baulast ein Institut des in die Kompetenz des Landesgesetzgebers fallenden '
-                         'bauaufsichtlichen Verfahrens ist, der deshalb auch die formellen und materiellen Voraus'
-                         'setzungen ihres Entstehens und Erlöschens bestimmt, darf sich die übernommene Belastung'
-                         ' auch auf die Nutzung des Grundstücks in bodenrechtlicher ______________________ Hinsic'
-                         'ht beziehen ____________________________________________________________________________________________________________________________________.',
-                         self.extractor.clean_text_for_tokenizer(text))
-
-
+        self.assertEqual(
+            "Obgleich die Baulast ein Institut des in die Kompetenz des Landesgesetzgebers fallenden "
+            "bauaufsichtlichen Verfahrens ist, der deshalb auch die formellen und materiellen Voraus"
+            "setzungen ihres Entstehens und Erlöschens bestimmt, darf sich die übernommene Belastung"
+            " auch auf die Nutzung des Grundstücks in bodenrechtlicher ______________________ Hinsic"
+            "ht beziehen ____________________________________________________________________________________________________________________________________.",
+            self.extractor.clean_text_for_tokenizer(text),
+        )
 
     def test_extract_case_refs_detail(self):
         fixtures = [
-
             {
-                'content': 'Rückwirkend zum 01.01.2014 trat das Gesetz zur Neufassung des Landesplanungsgesetzes (LaplaG) und zur Aufhebung '
-                           'des Landesentwicklungsgrundsätzegesetzes'
-                           ' vom 27.01.2014 (GVOBl. 12). Das OVG Schleswig habe bereits in seinem Urteil vom 22.04.2010 (1 KN 19/09) zur '
-                           'im Wesentlichen gleichlautenden Vorgängervorschrift im LROP-TF 2004 festgestellt, dass dieser Vorschrift die'
-                           ' erforderliche Bestimmtheit bzw. Bestimmbarkeit und damit die Zielqualität nicht zukomme.',
-                'refs': [
-                    Ref(ref_type=RefType.CASE, court='OVG Schleswig', file_number='1 KN 19/09')  # , date=date(2010, 4, 22)
-                ]
+                "content": "Rückwirkend zum 01.01.2014 trat das Gesetz zur Neufassung des Landesplanungsgesetzes (LaplaG) und zur Aufhebung "
+                "des Landesentwicklungsgrundsätzegesetzes"
+                " vom 27.01.2014 (GVOBl. 12). Das OVG Schleswig habe bereits in seinem Urteil vom 22.04.2010 (1 KN 19/09) zur "
+                "im Wesentlichen gleichlautenden Vorgängervorschrift im LROP-TF 2004 festgestellt, dass dieser Vorschrift die"
+                " erforderliche Bestimmtheit bzw. Bestimmbarkeit und damit die Zielqualität nicht zukomme.",
+                "refs": [
+                    Ref(
+                        ref_type=RefType.CASE,
+                        court="OVG Schleswig",
+                        file_number="1 KN 19/09",
+                    )  # , date=date(2010, 4, 22)
+                ],
             }
         ]
 
@@ -100,7 +117,7 @@ class CaseRefExTest(BaseRefExTest):
         matched = 0
         not_matched = 0
 
-        with open(os.path.join(self.resource_dir, 'case/file_numbers.txt')) as f:
+        with open(os.path.join(self.resource_dir, "case/file_numbers.txt")) as f:
             for line in f.readlines():
                 line = line.strip()
                 if not re.search(pattern, line):
@@ -109,62 +126,224 @@ class CaseRefExTest(BaseRefExTest):
                 else:
                     matched += 1
 
-        print('matched: %i' % matched)
-        print('not_matched: %i' % not_matched)
+        print("matched: %i" % matched)
+        print("not_matched: %i" % not_matched)
 
         print(pattern)
 
-    @skip
-    def test_extract(self):
-        self.assert_refs([
-            {
-                'resource': 'bsg_2018-06-27.txt',
-                'refs': [
-                    Ref(ref_type=RefType.CASE, court='BGH', file_number='6 KA 45/13'),
-                    Ref(ref_type=RefType.CASE, court='BGH', file_number='IX ZR 165/12'),
-                    Ref(ref_type=RefType.CASE, court='BGH', file_number='IX ZR 165/12'),
-                    Ref(ref_type=RefType.CASE, court='BVerfG', file_number='1 BvL 7/14'),
-                    Ref(ref_type=RefType.CASE, court='LSG Nordrhein-Westfalen', file_number='11 KA 67/10'),
-                    Ref(ref_type=RefType.CASE, court='LSG Nordrhein-Westfalen', file_number='24 K 120/10'),
-                    Ref(ref_type=RefType.CASE, court='LSG Nordrhein-Westfalen', file_number='7 U 199/05'),
-                    Ref(ref_type=RefType.CASE, court='OLG Koblenz', file_number='19 U 98/97'),
-                    Ref(ref_type=RefType.CASE, court='OLG Koblenz', file_number='2 U 553/13'),
-                    Ref(ref_type=RefType.CASE, court='OLG Koblenz', file_number='6 KA 39/17'),
-                    Ref(ref_type=RefType.CASE, court='OLG Koblenz', file_number='6 KA 40/17'),
-                    Ref(ref_type=RefType.CASE, court='OLG Koblenz', file_number='IX ZR 103/14'),
-                    # Ref(ref_type=RefType.CASE, court='', file_number=''),
-                    # Ref(ref_type=RefType.CASE, court='', file_number=''),
-
-                    # Ref(ref_type=RefType.LAW, book='baunvo', section='2'),
-                    # Ref(ref_type=RefType.LAW, book='baunvo', section='3'),
-                    # Ref(ref_type=RefType.LAW, book='baunvo', section='4'),
-
-                ]
-            }
-        ])
-
-    @skip
-    def test_extract_html(self):
-        self.assert_refs([
-            {
-                'resource': 'bsg_2018-06-27.html',
-                'refs': [
-                    Ref(ref_type=RefType.CASE, court='BGH', file_number='6 KA 45/13'),
-                    Ref(ref_type=RefType.CASE, court='BGH', file_number='IX ZR 165/12'),
-                    Ref(ref_type=RefType.CASE, court='BGH', file_number='IX ZR 165/12'),
-                    Ref(ref_type=RefType.CASE, court='BVerfG', file_number='1 BvL 7/14'),
-                    Ref(ref_type=RefType.CASE, court='LSG Nordrhein-Westfalen', file_number='11 KA 67/10'),
-                    Ref(ref_type=RefType.CASE, court='LSG Nordrhein-Westfalen', file_number='24 K 120/10'),
-                    Ref(ref_type=RefType.CASE, court='LSG Nordrhein-Westfalen', file_number='7 U 199/05'),
-                    Ref(ref_type=RefType.CASE, court='OLG Koblenz', file_number='19 U 98/97'),
-                    Ref(ref_type=RefType.CASE, court='OLG Koblenz', file_number='2 U 553/13'),
-                    Ref(ref_type=RefType.CASE, court='OLG Koblenz', file_number='6 KA 39/17'),
-                    Ref(ref_type=RefType.CASE, court='OLG Koblenz', file_number='6 KA 40/17'),
-                    Ref(ref_type=RefType.CASE, court='OLG Koblenz', file_number='IX ZR 103/14'),
-
-                ]
-            }
-        ])
+    def test_extract_from_bsg_case_1(self):
+        self.assert_refs(
+            [
+                {
+                    "resource": "bsg_2018-06-27.txt",
+                    "refs": [
+                        Ref(
+                            ref_type=RefType.CASE,
+                            court="Bundessozialgericht",
+                            file_number="B 6 KA 45/13 R",
+                        ),
+                        Ref(
+                            ref_type=RefType.CASE,
+                            court="BGH",
+                            file_number="IX ZR 165/12",
+                        ),
+                        Ref(
+                            ref_type=RefType.CASE,
+                            court="BGH",
+                            file_number="IX ZR 165/12",
+                        ),
+                        Ref(
+                            ref_type=RefType.CASE,
+                            court="BVerfG",
+                            file_number="1 BvL 7/14",
+                        ),
+                        Ref(
+                            ref_type=RefType.CASE,
+                            court="LSG Nordrhein-Westfalen",
+                            file_number="L 11 KA 67/10",
+                        ),
+                        Ref(
+                            ref_type=RefType.CASE,
+                            court="LSG Nordrhein-Westfalen",
+                            file_number="L 24 K 120/10",
+                        ),
+                        Ref(
+                            ref_type=RefType.CASE,
+                            court="OLG Koblenz",  # ERROR: It's actually "Brandenburgische OLG"
+                            file_number="7 U 199/05",
+                        ),
+                        Ref(
+                            ref_type=RefType.CASE,
+                            court="OLG Hamm",
+                            file_number="19 U 98/97",
+                        ),
+                        Ref(
+                            ref_type=RefType.CASE,
+                            court="OLG Koblenz",
+                            file_number="2 U 553/13",
+                        ),
+                        Ref(
+                            ref_type=RefType.CASE,
+                            court="Bundessozialgericht",
+                            file_number="B 6 KA 39/17 R",
+                        ),
+                        Ref(
+                            ref_type=RefType.CASE,
+                            court="Bundessozialgericht",
+                            file_number="B 6 KA 40/17 R",
+                        ),
+                        Ref(
+                            ref_type=RefType.CASE,
+                            court="OLG Koblenz",
+                            file_number="IX ZR 103/14",
+                        ),
+                        # Ref(ref_type=RefType.CASE, court='', file_number=''),
+                        # Ref(ref_type=RefType.CASE, court='', file_number=''),
+                        # Ref(ref_type=RefType.LAW, book='baunvo', section='2'),
+                        # Ref(ref_type=RefType.LAW, book='baunvo', section='3'),
+                        # Ref(ref_type=RefType.LAW, book='baunvo', section='4'),
+                    ],
+                }
+            ]
+        )
+
+    def test_from_bsg_case_1_html(self):
+        self.assert_refs(
+            [
+                {
+                    "resource": "bsg_2018-06-27.txt",
+                    "refs": [
+                        Ref(
+                            ref_type=RefType.CASE,
+                            court="Bundessozialgericht",
+                            file_number="B 6 KA 45/13 R",
+                        ),
+                        Ref(
+                            ref_type=RefType.CASE,
+                            court="BGH",
+                            file_number="IX ZR 165/12",
+                        ),
+                        Ref(
+                            ref_type=RefType.CASE,
+                            court="BGH",
+                            file_number="IX ZR 165/12",
+                        ),
+                        Ref(
+                            ref_type=RefType.CASE,
+                            court="BVerfG",
+                            file_number="1 BvL 7/14",
+                        ),
+                        Ref(
+                            ref_type=RefType.CASE,
+                            court="LSG Nordrhein-Westfalen",
+                            file_number="L 11 KA 67/10",
+                        ),
+                        Ref(
+                            ref_type=RefType.CASE,
+                            court="LSG Nordrhein-Westfalen",
+                            file_number="L 24 K 120/10",
+                        ),
+                        Ref(
+                            ref_type=RefType.CASE,
+                            court="OLG Koblenz",  # ERROR: It's actually "Brandenburgische OLG"
+                            file_number="7 U 199/05",
+                        ),
+                        Ref(
+                            ref_type=RefType.CASE,
+                            court="OLG Hamm",
+                            file_number="19 U 98/97",
+                        ),
+                        Ref(
+                            ref_type=RefType.CASE,
+                            court="OLG Koblenz",
+                            file_number="2 U 553/13",
+                        ),
+                        Ref(
+                            ref_type=RefType.CASE,
+                            court="Bundessozialgericht",
+                            file_number="B 6 KA 39/17 R",
+                        ),
+                        Ref(
+                            ref_type=RefType.CASE,
+                            court="Bundessozialgericht",
+                            file_number="B 6 KA 40/17 R",
+                        ),
+                        Ref(
+                            ref_type=RefType.CASE,
+                            court="OLG Koblenz",
+                            file_number="IX ZR 103/14",
+                        ),
+                        # Ref(ref_type=RefType.CASE, court='', file_number=''),
+                        # Ref(ref_type=RefType.CASE, court='', file_number=''),
+                        # Ref(ref_type=RefType.LAW, book='baunvo', section='2'),
+                        # Ref(ref_type=RefType.LAW, book='baunvo', section='3'),
+                        # Ref(ref_type=RefType.LAW, book='baunvo', section='4'),
+                    ],
+                }
+            ]
+        )
+
+    def test_potential_false_positives_should_not_be_matched(self):
+        """Those false positives with invalid case codes should not be extracted,
+        see also https://github.com/openlegaldata/legal-reference-extraction/issues/4
+        The case regex uses some heuristics to filter those out.
+        """
+        self.assert_refs(
+            [
+                {
+                    "content": "Ein Satz mit 2014 und 2014/20, und weiteren Sachen",
+                    "refs": [],
+                },
+                {
+                    "content": "Ein Satz mit 2000 bis 07/20, und weiteren Sachen",
+                    "refs": [],
+                },
+                {
+                    "content": "Ein Satz mit 2019 bis KW 44/20, und weiteren Sachen",
+                    "refs": [],
+                },
+                {
+                    "content": "Ein Satz mit 49364 Reifen 245/45, und weiteren Sachen",
+                    "refs": [],
+                },
+            ]
+        )
+
+    def test_bverwg_cases(self):
+        self.assert_refs(
+            [
+                {
+                    "content": "BVerwG 7 A 9.19 - Urteil vom 15. Oktober 2020",
+                    "refs": [
+                        Ref(
+                            ref_type=RefType.CASE,
+                            court="BVerwG",
+                            file_number="7 A 9.19",
+                        )
+                    ],
+                },
+                {
+                    "content": "10 C 23.12",
+                    "refs": [
+                        Ref(
+                            ref_type=RefType.CASE,
+                            court="",
+                            file_number="10 C 23.12",
+                        )
+                    ],
+                },
+            ]
+        )
 
     def test_get_codes(self):
         self.extractor.get_codes()
+
+    def test_codes_are_correctly_extracted(self):
+        """If a code contains some extra information in brackets (e.g. 'W (pat)')
+        this part is not being returned, since it is not in the 'code' capture group
+        of the case regex."""
+        codes = self.extractor.get_codes()
+        self.assertIn("Ks", codes)
+        self.assertIn("AnwSt", codes)
+        self.assertIn("W", codes)
+        self.assertIn("REMiet", codes)
diff --git a/refex/tests/test_extractor.py b/refex/tests/test_extractor.py
index 5cee66d..8f9c178 100644
--- a/refex/tests/test_extractor.py
+++ b/refex/tests/test_extractor.py
@@ -13,39 +13,34 @@ class RefExTest(BaseRefExTest):
         pass
 
     def test_extract(self):
-        content_html = '<h1>Hallo</h1><p>Ein Satz mit § 3 Abs. 1 Nr. 1 i.V.m. § 3b AsylG, und weiteren Sachen.</p>'
-        content_html += '<p>Komplexe Zitate gibt es auch §§ 3, 3b AsylG.</p>'
-        content_html += '<p>Beschluss des OLG Koblenz zurückgewiesen <em>(Beschluss vom 29.10.2015 - IX ZR 103/14)</em>; diese Entscheidung beruht jedoch auf der vom BGH beanstandeten unzureichenden Darlegung der grundsätzlichen Bedeutung. Nähere Ausführungen zur Wirksamkeit</p>'
+        content_html = "<h1>Hallo</h1><p>Ein Satz mit § 3 Abs. 1 Nr. 1 i.V.m. § 3b AsylG, und weiteren Sachen.</p>"
+        content_html += "<p>Komplexe Zitate gibt es auch §§ 3, 3b AsylG.</p>"
+        content_html += "<p>Beschluss des OLG Koblenz zurückgewiesen <em>(Beschluss vom 29.10.2015 - IX ZR 103/14)</em>; diese Entscheidung beruht jedoch auf der vom BGH beanstandeten unzureichenden Darlegung der grundsätzlichen Bedeutung. Nähere Ausführungen zur Wirksamkeit</p>"
 
         new_content, markers = self.extractor.extract(content_html)
 
         print(new_content)
 
-        print('Markers:')
+        print("Markers:")
         for m in markers:
-            print('- %s' % m.text)
+            print("- %s" % m.text)
 
     @skip
     def test_extract_bsg(self):
 
-        self.assert_refs([
-            {
-                'resource': 'bsg_2018-06-27.txt',
-                'refs': [
-
-                ]
-            }
-        ])
+        self.assert_refs([{"resource": "bsg_2018-06-27.txt", "refs": []}])
 
     def test_catastrophic_backtracking(self):
         self.extractor.law_book_codes = self.get_book_codes_from_file()
-        self.assert_refs([
-            {
-                'resource': 'bgh_2018-08-16.html',
-                'refs': [
-                    # catastrophic_backtracking
-                    Ref(ref_type=RefType.LAW, book='zpo', section='253'),
-                    # Ref(ref_type=RefType.CASE, file_number='III ZR 126/15')
-                ]
-            }
-        ])
\ No newline at end of file
+        self.assert_refs(
+            [
+                {
+                    "resource": "bgh_2018-08-16.html",
+                    "refs": [
+                        # catastrophic_backtracking
+                        Ref(ref_type=RefType.LAW, book="zpo", section="253"),
+                        # Ref(ref_type=RefType.CASE, file_number='III ZR 126/15')
+                    ],
+                }
+            ]
+        )
diff --git a/refex/tests/test_law_extractor.py b/refex/tests/test_law_extractor.py
index c7f3eca..68bcd58 100644
--- a/refex/tests/test_law_extractor.py
+++ b/refex/tests/test_law_extractor.py
@@ -8,7 +8,6 @@ from refex.tests import BaseRefExTest
 
 
 class LawRefExTest(BaseRefExTest):
-
     def setUp(self):
         self.extractor.do_law_refs = True
         self.extractor.do_case_refs = False
@@ -41,28 +40,30 @@ class LawRefExTest(BaseRefExTest):
 
         :return:
         """
-        content_html = '<h1>Hallo</h1><p>Ein Satz mit § 3 Abs. 1 Nr. 1 i.V.m. § 3b AsylG, und weiteren Sachen.</p>'
-        content_html += '<p>Komplexe Zitate gibt es auch §§ 3, 3b AsylG.</p>'
+        content_html = "<h1>Hallo</h1><p>Ein Satz mit § 3 Abs. 1 Nr. 1 i.V.m. § 3b AsylG, und weiteren Sachen.</p>"
+        content_html += "<p>Komplexe Zitate gibt es auch §§ 3, 3b AsylG.</p>"
 
         new_content, markers = self.extractor.extract(content_html)
 
         print(new_content)
-        print('Markers: %s' % markers)
+        print("Markers: %s" % markers)
 
     def test_with_law_book_context(self):
         """Book context is used for extracting law references from within law text, where book is not
         explicitly mentioned."""
 
-        self.extractor.law_book_context = 'bgb'
+        self.extractor.law_book_context = "bgb"
 
-        text = '<P>(2) Der Vorsitzende kann einen solchen Vertreter auch bestellen,' \
-               ' wenn in den Fällen des § 20 eine nicht prozessfähige Person bei dem ' \
-               'Gericht ihres Aufenthaltsortes verklagt werden soll..</P>'
+        text = (
+            "<P>(2) Der Vorsitzende kann einen solchen Vertreter auch bestellen,"
+            " wenn in den Fällen des § 20 eine nicht prozessfähige Person bei dem "
+            "Gericht ihres Aufenthaltsortes verklagt werden soll..</P>"
+        )
 
         new_content, markers = self.extractor.extract(text)
 
-        self.assertEqual(1, len(markers), 'Invalid marker count')
-        self.assertEqual('20', markers[0].references[0].section, 'Invalid section')
+        self.assertEqual(1, len(markers), "Invalid marker count")
+        self.assertEqual("20", markers[0].references[0].section, "Invalid section")
 
     # def test_handle_multiple_law_refs(self):
     #     ref_str = '§§ 10000 Abs. 3 ZPO, 151, 153 VwGO'
@@ -81,375 +82,414 @@ class LawRefExTest(BaseRefExTest):
     def test_timeout_ref(self):
         expected = [
             {
-                'content': ' Auslandsaufenthalt mit beachtlicher Wahrscheinlichkeit aufgrund eines ihm (zugeschriebenen) Verfolgungsgrundes '
-                           'im Sinne des § 3 Abs. 1 AsylG, insbesondere einer regimekritischen politischen Überzeugung, erfolgen würden. '
-                           'Nach der Rechtsprechung des schleswig-holsteinischen Oberverwaltungsgerichtes (Urteil vom 23.11.2016, - 3 LB 17/16 -, juris), '
-                           'der sich die Kammer anschließt, besteht nach der gegenwärtigen Erkenntnislage keine hinreichende'
-                           ' Grundlage für die Annahme, dass der totalitäre syrische Staat jeden Rückkehrer pauschal unter eine '
-                           'Art Generalsverdacht stellt, der Opposition anzugehören (so auch OVG Saarland, '
-                           'Urteil vom 2.2.2017, - 2 A 515/16 -; OVG Rheinland-Pfalz, Urteil vom 16.12.2016, -1A 10922/16 -; Bayrischer VGH, '
-                           'Urteil vom 12.12.16, - 21 B 16.30364; OVG Nordrhein-Westfalen,',
-                'refs': [
-                    Ref(ref_type=RefType.LAW, book='asylg', section='3'),
-                ]
+                "content": " Auslandsaufenthalt mit beachtlicher Wahrscheinlichkeit aufgrund eines ihm (zugeschriebenen) Verfolgungsgrundes "
+                "im Sinne des § 3 Abs. 1 AsylG, insbesondere einer regimekritischen politischen Überzeugung, erfolgen würden. "
+                "Nach der Rechtsprechung des schleswig-holsteinischen Oberverwaltungsgerichtes (Urteil vom 23.11.2016, - 3 LB 17/16 -, juris), "
+                "der sich die Kammer anschließt, besteht nach der gegenwärtigen Erkenntnislage keine hinreichende"
+                " Grundlage für die Annahme, dass der totalitäre syrische Staat jeden Rückkehrer pauschal unter eine "
+                "Art Generalsverdacht stellt, der Opposition anzugehören (so auch OVG Saarland, "
+                "Urteil vom 2.2.2017, - 2 A 515/16 -; OVG Rheinland-Pfalz, Urteil vom 16.12.2016, -1A 10922/16 -; Bayrischer VGH, "
+                "Urteil vom 12.12.16, - 21 B 16.30364; OVG Nordrhein-Westfalen,",
+                "refs": [
+                    Ref(ref_type=RefType.LAW, book="asylg", section="3"),
+                ],
             }
         ]
 
         self.assert_refs(expected)
 
     def test_extract2(self):
-        self.assert_refs([
-            {
-                'resource': 'law/extract2.txt',
-                'refs': [
-                    Ref(ref_type=RefType.LAW, book='vwgo', section='124'),
-                    Ref(ref_type=RefType.LAW, book='vwgo', section='124a'),
-                ]
-            }
-        ])
+        self.assert_refs(
+            [
+                {
+                    "resource": "law/extract2.txt",
+                    "refs": [
+                        Ref(ref_type=RefType.LAW, book="vwgo", section="124"),
+                        Ref(ref_type=RefType.LAW, book="vwgo", section="124a"),
+                    ],
+                }
+            ]
+        )
 
     def test_extract3(self):
-        self.assert_refs([
-            {
-                'resource': 'law/extract3.txt',
-                'refs': [
-                    Ref(ref_type=RefType.LAW, book='vwgo', section='167'),
-                    Ref(ref_type=RefType.LAW, book='zpo', section='708'),
-                    Ref(ref_type=RefType.LAW, book='zpo', section='711'),
-                ]
-            }
-        ])
+        self.assert_refs(
+            [
+                {
+                    "resource": "law/extract3.txt",
+                    "refs": [
+                        Ref(ref_type=RefType.LAW, book="vwgo", section="167"),
+                        Ref(ref_type=RefType.LAW, book="zpo", section="708"),
+                        Ref(ref_type=RefType.LAW, book="zpo", section="711"),
+                    ],
+                }
+            ]
+        )
 
     def test_extract4(self):
-        self.assert_refs([
-            {
-                'resource': 'law/extract4.txt',
-                'refs': [
-                    Ref(ref_type=RefType.LAW, book='baugb', section='34'),
-                    Ref(ref_type=RefType.LAW, book='baunvo', section='2'),
-                    Ref(ref_type=RefType.LAW, book='baunvo', section='3'),
-                    Ref(ref_type=RefType.LAW, book='baunvo', section='4'),
-
-                ]
-            }
-        ])
+        self.assert_refs(
+            [
+                {
+                    "resource": "law/extract4.txt",
+                    "refs": [
+                        Ref(ref_type=RefType.LAW, book="baugb", section="34"),
+                        Ref(ref_type=RefType.LAW, book="baunvo", section="2"),
+                        Ref(ref_type=RefType.LAW, book="baunvo", section="3"),
+                        Ref(ref_type=RefType.LAW, book="baunvo", section="4"),
+                    ],
+                }
+            ]
+        )
 
     def test_extract5(self):
-        self.assert_refs([
-            {
-                'resource': 'law/extract5.txt',
-                'refs': [
-                    Ref(ref_type=RefType.LAW, book='vwgo', section='154'),
-                    Ref(ref_type=RefType.LAW, book='vwgo', section='154'),
-                    Ref(ref_type=RefType.LAW, book='vwgo', section='162'),
-                ]
-            }
-        ])
+        self.assert_refs(
+            [
+                {
+                    "resource": "law/extract5.txt",
+                    "refs": [
+                        Ref(ref_type=RefType.LAW, book="vwgo", section="154"),
+                        Ref(ref_type=RefType.LAW, book="vwgo", section="154"),
+                        Ref(ref_type=RefType.LAW, book="vwgo", section="162"),
+                    ],
+                }
+            ]
+        )
 
     def test_extract6(self):
-        self.assert_refs([
-            {
-                'resource': 'law/extract6.txt',
-                'refs': [
-                    Ref(ref_type=RefType.LAW, book='vwgo', section='42')
-                ]
-            }
-        ])
-
+        self.assert_refs(
+            [
+                {
+                    "resource": "law/extract6.txt",
+                    "refs": [Ref(ref_type=RefType.LAW, book="vwgo", section="42")],
+                }
+            ]
+        )
 
     def test_extract7(self):
-        self.assert_refs([
-            {
-                'resource': 'law/extract7.txt',
-                'refs': [
-                    Ref(ref_type=RefType.LAW, book='vwgo', section='154'),
-                    Ref(ref_type=RefType.LAW, book='vwgo', section='154'),
-                    Ref(ref_type=RefType.LAW, book='vwgo', section='162'),
-                ]
-            }
-        ])
+        self.assert_refs(
+            [
+                {
+                    "resource": "law/extract7.txt",
+                    "refs": [
+                        Ref(ref_type=RefType.LAW, book="vwgo", section="154"),
+                        Ref(ref_type=RefType.LAW, book="vwgo", section="154"),
+                        Ref(ref_type=RefType.LAW, book="vwgo", section="162"),
+                    ],
+                }
+            ]
+        )
 
     def test_extract8(self):
-        self.assert_refs([
-            {
-                'resource': 'law/extract8.txt',
-                'refs': [
-                    # § 77 Abs. 1 Satz 1, 1. Halbsatz AsylG
-                    Ref(ref_type=RefType.LAW, book='asylg', section='77')
-                ]
-            }
-        ])
+        self.assert_refs(
+            [
+                {
+                    "resource": "law/extract8.txt",
+                    "refs": [
+                        # § 77 Abs. 1 Satz 1, 1. Halbsatz AsylG
+                        Ref(ref_type=RefType.LAW, book="asylg", section="77")
+                    ],
+                }
+            ]
+        )
 
     def test_extract9(self):
-        self.assert_refs([
-            {
-                'resource': 'law/extract9.txt',
-                'refs': [
-                    # §§ 52 Abs. 1; 53 Abs. 2 Nr. 1; 63 Abs. 2 StPO
-                    Ref(ref_type=RefType.LAW, book='stpo', section='52'),
-                    Ref(ref_type=RefType.LAW, book='stpo', section='53'),
-                    Ref(ref_type=RefType.LAW, book='stpo', section='63'),
-
-                ]
-            }
-        ])
+        self.assert_refs(
+            [
+                {
+                    "resource": "law/extract9.txt",
+                    "refs": [
+                        # §§ 52 Abs. 1; 53 Abs. 2 Nr. 1; 63 Abs. 2 StPO
+                        Ref(ref_type=RefType.LAW, book="stpo", section="52"),
+                        Ref(ref_type=RefType.LAW, book="stpo", section="53"),
+                        Ref(ref_type=RefType.LAW, book="stpo", section="63"),
+                    ],
+                }
+            ]
+        )
 
     @skip
     def test_extract10(self):
-        self.assert_refs([
-            {
-                'resource': 'law/extract10.txt',
-                'refs': [
-                    # Art 12 Abs 1 GG
-                    Ref(ref_type=RefType.LAW, book='gg', section='1'),
-                    Ref(ref_type=RefType.LAW, book='gg', section='2'),
-                    Ref(ref_type=RefType.LAW, book='gg', section='3'),
-                    Ref(ref_type=RefType.LAW, book='gg', section='12'),
-
-                    # Ref(ref_type=RefType.LAW, book='stpo', section='53'),
-                    # Ref(ref_type=RefType.LAW, book='stpo', section='63'),
-
-                ]
-            }
-        ])
+        self.assert_refs(
+            [
+                {
+                    "resource": "law/extract10.txt",
+                    "refs": [
+                        # Art 12 Abs 1 GG
+                        Ref(ref_type=RefType.LAW, book="gg", section="1"),
+                        Ref(ref_type=RefType.LAW, book="gg", section="2"),
+                        Ref(ref_type=RefType.LAW, book="gg", section="3"),
+                        Ref(ref_type=RefType.LAW, book="gg", section="12"),
+                        # Ref(ref_type=RefType.LAW, book='stpo', section='53'),
+                        # Ref(ref_type=RefType.LAW, book='stpo', section='63'),
+                    ],
+                }
+            ]
+        )
 
     # @skip
     def test_extract11(self):
-        self.assert_refs([
-            {
-                'resource': 'law/extract11.txt',
-                'refs': [
-                    # §§ 556d, 556g BGB
-                    Ref(ref_type=RefType.LAW, book='bgb', section='556d'),
-                    Ref(ref_type=RefType.LAW, book='bgb', section='556e'),
-
-                ]
-            }
-        ])
+        self.assert_refs(
+            [
+                {
+                    "resource": "law/extract11.txt",
+                    "refs": [
+                        # §§ 556d, 556g BGB
+                        Ref(ref_type=RefType.LAW, book="bgb", section="556d"),
+                        Ref(ref_type=RefType.LAW, book="bgb", section="556e"),
+                    ],
+                }
+            ]
+        )
 
     def test_extract12(self):
-        self.assert_refs([
-            {
-                'resource': 'law/extract12.txt',
-                'refs': [
-                    # §§ 1, 2 Abs. 2, 3, 10 Abs. 1 Nr. 1 BGB
-                    Ref(ref_type=RefType.LAW, book='bgb', section='1'),
-                    Ref(ref_type=RefType.LAW, book='bgb', section='2'),
-                    Ref(ref_type=RefType.LAW, book='bgb', section='3'),
-                    Ref(ref_type=RefType.LAW, book='bgb', section='10'),
-
-                ]
-            }
-        ])
+        self.assert_refs(
+            [
+                {
+                    "resource": "law/extract12.txt",
+                    "refs": [
+                        # §§ 1, 2 Abs. 2, 3, 10 Abs. 1 Nr. 1 BGB
+                        Ref(ref_type=RefType.LAW, book="bgb", section="1"),
+                        Ref(ref_type=RefType.LAW, book="bgb", section="2"),
+                        Ref(ref_type=RefType.LAW, book="bgb", section="3"),
+                        Ref(ref_type=RefType.LAW, book="bgb", section="10"),
+                    ],
+                }
+            ]
+        )
 
     def test_extract13(self):
-        self.assert_refs([
-            {
-                'resource': 'law/extract13.txt',
-                'refs': [
-                    # § 3d AsylG, aber auch § 123 VwGO. ... auch §§ 3, 3b AsylG
-                    Ref(ref_type=RefType.LAW, book='asylg', section='3'),
-                    Ref(ref_type=RefType.LAW, book='asylg', section='3b'),
-                    Ref(ref_type=RefType.LAW, book='asylg', section='3d'),
-                    Ref(ref_type=RefType.LAW, book='vwgo', section='123'),
-
-                ]
-            }
-        ])
+        self.assert_refs(
+            [
+                {
+                    "resource": "law/extract13.txt",
+                    "refs": [
+                        # § 3d AsylG, aber auch § 123 VwGO. ... auch §§ 3, 3b AsylG
+                        Ref(ref_type=RefType.LAW, book="asylg", section="3"),
+                        Ref(ref_type=RefType.LAW, book="asylg", section="3b"),
+                        Ref(ref_type=RefType.LAW, book="asylg", section="3d"),
+                        Ref(ref_type=RefType.LAW, book="vwgo", section="123"),
+                    ],
+                }
+            ]
+        )
 
     def test_extract14(self):
-        self.assert_refs([
-            {
-                'resource': 'law/extract14.txt',
-                'refs': [
-                    # duplicated book code parts
-                    Ref(ref_type=RefType.LAW, book='sgg', section='136'),
-                    Ref(ref_type=RefType.LAW, book='sgb x', section='48'),
-
-                ]
-            }
-        ])
+        self.assert_refs(
+            [
+                {
+                    "resource": "law/extract14.txt",
+                    "refs": [
+                        # duplicated book code parts
+                        Ref(ref_type=RefType.LAW, book="sgg", section="136"),
+                        Ref(ref_type=RefType.LAW, book="sgb x", section="48"),
+                    ],
+                }
+            ]
+        )
 
     def test_extract15(self):
-        self.assert_refs([
-            {
-                'resource': 'law/extract15.txt',
-                'refs': [
-                    Ref(ref_type=RefType.LAW, book='vwgo', section='124'),
-                    Ref(ref_type=RefType.LAW, book='vwgo', section='124a'),
-                ]
-            }
-        ], True)
+        self.assert_refs(
+            [
+                {
+                    "resource": "law/extract15.txt",
+                    "refs": [
+                        Ref(ref_type=RefType.LAW, book="vwgo", section="124"),
+                        Ref(ref_type=RefType.LAW, book="vwgo", section="124a"),
+                    ],
+                }
+            ],
+            True,
+        )
 
     def test_extract16(self):
-        self.assert_refs([
-            {
-                'resource': 'law/extract16.txt',
-                'refs': [
-                    Ref(ref_type=RefType.LAW, book='vwgo', section='167'),
-                    Ref(ref_type=RefType.LAW, book='zpo', section='708'),
-                    Ref(ref_type=RefType.LAW, book='zpo', section='711'),
-                ]
-            }
-        ], True)
+        self.assert_refs(
+            [
+                {
+                    "resource": "law/extract16.txt",
+                    "refs": [
+                        Ref(ref_type=RefType.LAW, book="vwgo", section="167"),
+                        Ref(ref_type=RefType.LAW, book="zpo", section="708"),
+                        Ref(ref_type=RefType.LAW, book="zpo", section="711"),
+                    ],
+                }
+            ],
+            True,
+        )
 
     def test_extract17(self):
-        self.assert_refs([
-            {
-                'resource': 'law/extract17.txt',
-                'refs': [
-                    Ref(ref_type=RefType.LAW, book='baugb', section='34'),
-                    Ref(ref_type=RefType.LAW, book='baunvo', section='2'),
-                    Ref(ref_type=RefType.LAW, book='baunvo', section='3'),
-                    Ref(ref_type=RefType.LAW, book='baunvo', section='4'),
-
-                ]
-            }
-        ], True)
+        self.assert_refs(
+            [
+                {
+                    "resource": "law/extract17.txt",
+                    "refs": [
+                        Ref(ref_type=RefType.LAW, book="baugb", section="34"),
+                        Ref(ref_type=RefType.LAW, book="baunvo", section="2"),
+                        Ref(ref_type=RefType.LAW, book="baunvo", section="3"),
+                        Ref(ref_type=RefType.LAW, book="baunvo", section="4"),
+                    ],
+                }
+            ],
+            True,
+        )
 
     def test_extract18(self):
-        self.assert_refs([
-            {
-                'resource': 'law/extract18.txt',
-                'refs': [
-                    Ref(ref_type=RefType.LAW, book='vwgo', section='154'),
-                    Ref(ref_type=RefType.LAW, book='vwgo', section='154'),
-                    Ref(ref_type=RefType.LAW, book='vwgo', section='162'),
-                ]
-            }
-        ], True)
+        self.assert_refs(
+            [
+                {
+                    "resource": "law/extract18.txt",
+                    "refs": [
+                        Ref(ref_type=RefType.LAW, book="vwgo", section="154"),
+                        Ref(ref_type=RefType.LAW, book="vwgo", section="154"),
+                        Ref(ref_type=RefType.LAW, book="vwgo", section="162"),
+                    ],
+                }
+            ],
+            True,
+        )
 
     def test_extract19(self):
-        self.assert_refs([
-            {
-                'resource': 'law/extract19.txt',
-                'refs': [
-                    Ref(ref_type=RefType.LAW, book='vwgo', section='42')
-                ]
-            }
-        ], True)
-
+        self.assert_refs(
+            [
+                {
+                    "resource": "law/extract19.txt",
+                    "refs": [Ref(ref_type=RefType.LAW, book="vwgo", section="42")],
+                }
+            ],
+            True,
+        )
 
     def test_extract20(self):
-        self.assert_refs([
-            {
-                'resource': 'law/extract20.txt',
-                'refs': [
-                    Ref(ref_type=RefType.LAW, book='vwgo', section='154'),
-                    Ref(ref_type=RefType.LAW, book='vwgo', section='154'),
-                    Ref(ref_type=RefType.LAW, book='vwgo', section='162'),
-                ]
-            }
-        ], True)
+        self.assert_refs(
+            [
+                {
+                    "resource": "law/extract20.txt",
+                    "refs": [
+                        Ref(ref_type=RefType.LAW, book="vwgo", section="154"),
+                        Ref(ref_type=RefType.LAW, book="vwgo", section="154"),
+                        Ref(ref_type=RefType.LAW, book="vwgo", section="162"),
+                    ],
+                }
+            ],
+            True,
+        )
 
     def test_extract21(self):
-        self.assert_refs([
-            {
-                'resource': 'law/extract21.txt',
-                'refs': [
-                    # § 77 Abs. 1 Satz 1, 1. Halbsatz AsylG
-                    Ref(ref_type=RefType.LAW, book='asylg', section='77')
-                ]
-            }
-        ], True)
+        self.assert_refs(
+            [
+                {
+                    "resource": "law/extract21.txt",
+                    "refs": [
+                        # § 77 Abs. 1 Satz 1, 1. Halbsatz AsylG
+                        Ref(ref_type=RefType.LAW, book="asylg", section="77")
+                    ],
+                }
+            ],
+            True,
+        )
 
     def test_extract22(self):
-        self.assert_refs([
-            {
-                'resource': 'law/extract22.txt',
-                'refs': [
-                    # §§ 52 Abs. 1; 53 Abs. 2 Nr. 1; 63 Abs. 2 StPO
-                    Ref(ref_type=RefType.LAW, book='stpo', section='52'),
-                    Ref(ref_type=RefType.LAW, book='stpo', section='53'),
-                    Ref(ref_type=RefType.LAW, book='stpo', section='63'),
-
-                ]
-            }
-        ], True)
+        self.assert_refs(
+            [
+                {
+                    "resource": "law/extract22.txt",
+                    "refs": [
+                        # §§ 52 Abs. 1; 53 Abs. 2 Nr. 1; 63 Abs. 2 StPO
+                        Ref(ref_type=RefType.LAW, book="stpo", section="52"),
+                        Ref(ref_type=RefType.LAW, book="stpo", section="53"),
+                        Ref(ref_type=RefType.LAW, book="stpo", section="63"),
+                    ],
+                }
+            ],
+            True,
+        )
 
     @skip
     def test_extract23(self):
-        self.assert_refs([
-            {
-                'resource': 'law/extract23.txt',
-                'refs': [
-                    # Art 12 Abs 1 GG
-                    Ref(ref_type=RefType.LAW, book='gg', section='1'),
-                    Ref(ref_type=RefType.LAW, book='gg', section='2'),
-                    Ref(ref_type=RefType.LAW, book='gg', section='3'),
-                    Ref(ref_type=RefType.LAW, book='gg', section='12'),
-
-                    # Ref(ref_type=RefType.LAW, book='stpo', section='53'),
-                    # Ref(ref_type=RefType.LAW, book='stpo', section='63'),
-
-                ]
-            }
-        ], True)
+        self.assert_refs(
+            [
+                {
+                    "resource": "law/extract23.txt",
+                    "refs": [
+                        # Art 12 Abs 1 GG
+                        Ref(ref_type=RefType.LAW, book="gg", section="1"),
+                        Ref(ref_type=RefType.LAW, book="gg", section="2"),
+                        Ref(ref_type=RefType.LAW, book="gg", section="3"),
+                        Ref(ref_type=RefType.LAW, book="gg", section="12"),
+                        # Ref(ref_type=RefType.LAW, book='stpo', section='53'),
+                        # Ref(ref_type=RefType.LAW, book='stpo', section='63'),
+                    ],
+                }
+            ],
+            True,
+        )
 
     # @skip
     def test_extract24(self):
-        self.assert_refs([
-            {
-                'resource': 'law/extract24.txt',
-                'refs': [
-                    # §§ 556d, 556g BGB
-                    Ref(ref_type=RefType.LAW, book='bgb', section='556d'),
-                    Ref(ref_type=RefType.LAW, book='bgb', section='556e'),
-
-                ]
-            }
-        ], True)
+        self.assert_refs(
+            [
+                {
+                    "resource": "law/extract24.txt",
+                    "refs": [
+                        # §§ 556d, 556g BGB
+                        Ref(ref_type=RefType.LAW, book="bgb", section="556d"),
+                        Ref(ref_type=RefType.LAW, book="bgb", section="556e"),
+                    ],
+                }
+            ],
+            True,
+        )
 
     def test_extract25(self):
-        self.assert_refs([
-            {
-                'resource': 'law/extract25.txt',
-                'refs': [
-                    # §§ 1, 2 Abs. 2, 3, 10 Abs. 1 Nr. 1 BGB
-                    Ref(ref_type=RefType.LAW, book='bgb', section='1'),
-                    Ref(ref_type=RefType.LAW, book='bgb', section='2'),
-                    Ref(ref_type=RefType.LAW, book='bgb', section='3'),
-                    Ref(ref_type=RefType.LAW, book='bgb', section='10'),
-
-                ]
-            }
-        ], True)
-
+        self.assert_refs(
+            [
+                {
+                    "resource": "law/extract25.txt",
+                    "refs": [
+                        # §§ 1, 2 Abs. 2, 3, 10 Abs. 1 Nr. 1 BGB
+                        Ref(ref_type=RefType.LAW, book="bgb", section="1"),
+                        Ref(ref_type=RefType.LAW, book="bgb", section="2"),
+                        Ref(ref_type=RefType.LAW, book="bgb", section="3"),
+                        Ref(ref_type=RefType.LAW, book="bgb", section="10"),
+                    ],
+                }
+            ],
+            True,
+        )
 
     def test_extract26(self):
-        self.assert_refs([
-            {
-                'resource': 'law/extract26.txt',
-                'refs': [
-                    # § 3d AsylG, aber auch § 123 VwGO. ... auch §§ 3, 3b AsylG
-                    Ref(ref_type=RefType.LAW, book='asylg', section='3'),
-                    Ref(ref_type=RefType.LAW, book='asylg', section='3b'),
-                    Ref(ref_type=RefType.LAW, book='asylg', section='3d'),
-                    Ref(ref_type=RefType.LAW, book='vwgo', section='123'),
-
-                ]
-            }
-        ], True)
+        self.assert_refs(
+            [
+                {
+                    "resource": "law/extract26.txt",
+                    "refs": [
+                        # § 3d AsylG, aber auch § 123 VwGO. ... auch §§ 3, 3b AsylG
+                        Ref(ref_type=RefType.LAW, book="asylg", section="3"),
+                        Ref(ref_type=RefType.LAW, book="asylg", section="3b"),
+                        Ref(ref_type=RefType.LAW, book="asylg", section="3d"),
+                        Ref(ref_type=RefType.LAW, book="vwgo", section="123"),
+                    ],
+                }
+            ],
+            True,
+        )
 
     def test_extract27(self):
-        self.assert_refs([
-            {
-                'resource': 'law/extract27.txt',
-                'refs': [
-                    # duplicated book code parts
-                    Ref(ref_type=RefType.LAW, book='sgg', section='136'),
-                    Ref(ref_type=RefType.LAW, book='sgb x', section='48'),
-
-                ]
-            }
-        ], True)
-
-
-
+        self.assert_refs(
+            [
+                {
+                    "resource": "law/extract27.txt",
+                    "refs": [
+                        # duplicated book code parts
+                        Ref(ref_type=RefType.LAW, book="sgg", section="136"),
+                        Ref(ref_type=RefType.LAW, book="sgb x", section="48"),
+                    ],
+                }
+            ],
+            True,
+        )
 
     def test_citation_styles(self):
         # TODO insert citation styles into text, random location, single and multiple occurences, test on marker text
-        with open(os.path.join(self.resource_dir, 'citation_styles.txt')) as f:
+        with open(os.path.join(self.resource_dir, "citation_styles.txt")) as f:
             x = DivideAndConquerLawRefExtractorMixin()
 
             content = f.read()
@@ -459,21 +499,20 @@ class LawRefExTest(BaseRefExTest):
 
             c = self.extractor.replace_content(content, markers)
 
-            print('-----\nOUT:\n %s' % c)
+            print("-----\nOUT:\n %s" % c)
 
-            print('\n\n\nIN:\n %s' % content)
+            print("\n\n\nIN:\n %s" % content)
 
-            self.assertEqual('', '')
+            self.assertEqual("", "")
 
             for marker in markers:
                 for ref in marker.get_references():
                     print(ref)
             # refs = [m.get_references() for m in markers]
 
-
     @skip
     def test_alternative_law_book_regex(self):
-        pattern = re.compile(r'([A-ZÄÜÖ][-ÄÜÖäüöA-Za-z]*)(V|G|O|B)')
-        for code in self.get_book_codes_from_file() + ['SGB X', 'SGG', 'SGB IV']:
+        pattern = re.compile(r"([A-ZÄÜÖ][-ÄÜÖäüöA-Za-z]*)(V|G|O|B)")
+        for code in self.get_book_codes_from_file() + ["SGB X", "SGG", "SGB IV"]:
             if not pattern.search(code):
                 print(code)
diff --git a/refex/tests/test_models.py b/refex/tests/test_models.py
index a38d1b8..ccafa29 100644
--- a/refex/tests/test_models.py
+++ b/refex/tests/test_models.py
@@ -11,13 +11,15 @@ class RefExModelsTest(TestCase):
         pass
 
     def test_replace_content(self):
-        text = '§ 123 ABC'
+        text = "§ 123 ABC"
         marker = RefMarker(text, 0, len(text))
-        marker.uuid = 'foo'
-        marker.references = [Ref(ref_type=RefType.LAW, book='abc', section=123)]
+        marker.uuid = "foo"
+        marker.references = [Ref(ref_type=RefType.LAW, book="abc", section=123)]
 
-        content = text + ' and other text...'
+        content = text + " and other text..."
 
-        self.assertEqual('[ref=foo]§ 123 ABC[/ref] and other text...',
-                         marker.replace_content(content, 0)[0],
-                         'Invalid content')
+        self.assertEqual(
+            "[ref=foo]§ 123 ABC[/ref] and other text...",
+            marker.replace_content(content, 0)[0],
+            "Invalid content",
+        )