Coverage for presidio_analyzer / predefined_recognizers / country_specific / germany / de_lanr_recognizer.py: 100%
22 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-03-29 09:03 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-03-29 09:03 +0000
1from typing import List, Optional
3from presidio_analyzer import Pattern, PatternRecognizer
6class DeLanrRecognizer(PatternRecognizer):
7 r"""
8 Recognizes German Lebenslange Arztnummer (LANR).
10 The LANR is a 9-digit lifetime physician number assigned by the
11 Kassenärztliche Vereinigung (KV) to every licensed physician participating
12 in the German statutory health insurance system (Vertragsarzt). It appears
13 on prescriptions (Rezepte), billing records, discharge letters, and other
14 statutory healthcare documents.
16 Legal basis: § 75 Abs. 7 SGB V (Sozialgesetzbuch Fünftes Buch).
17 Standard: KBV-Richtlinie nach § 75 Abs. 7 SGB V zur Vergabe der Arzt-,
18 Betriebsstätten-, Praxisnetz- sowie der Netzverbundnummern.
19 Data protection: DSGVO Art. 9 (Gesundheitsdaten), BDSG § 22.
21 Format (9 digits):
22 Pos 1–6: Arztnummer (physician identifier, assigned by KV)
23 Pos 7: Prüfziffer (check digit, derived from pos 1–6)
24 Pos 8–9: Arztgruppe / Fachgruppe (specialty / physician group code)
26 Examples (fictitious): 123456901, 234567601, 100000414
28 Check digit algorithm (KBV specification):
29 1. Apply weights [4, 9, 2, 10, 5, 3] to digits at positions 1–6.
30 2. For each product > 9, replace it with the cross-sum of its digits
31 (e.g. 18 → 1+8 = 9, 40 → 4+0 = 4).
32 3. Sum all six values.
33 4. Check digit (pos 7) = sum mod 10.
35 Accuracy note: The base pattern ``\\b\\d{9}\\b`` matches any 9-digit token.
36 Because LANRs share the same surface form as other 9-digit identifiers
37 (e.g. DE_BSNR), the checksum in ``validate_result()`` is the primary guard
38 against false positives; context words are required for high-confidence
39 results without a valid checksum. Formal accuracy evaluation has not been
40 performed on a labelled dataset.
42 :param patterns: List of patterns to be used by this recognizer
43 :param context: List of context words to increase confidence in detection
44 :param supported_language: Language this recognizer supports
45 :param supported_entity: The entity this recognizer can detect
46 """
48 PATTERNS = [
49 Pattern(
50 "Lebenslange Arztnummer LANR (9 digits)",
51 r"\b\d{9}\b",
52 0.3,
53 ),
54 ]
56 CONTEXT = [
57 "arztnummer",
58 "lanr",
59 "lebenslange arztnummer",
60 "arzt-nr",
61 "arzt nr",
62 "arzt-nummer",
63 "vertragsarzt",
64 "kassenarzt",
65 "niedergelassener arzt",
66 "kbv",
67 "kassenärztliche vereinigung",
68 "kv-nummer",
69 "rezept",
70 "verschreibung",
71 "behandelnder arzt",
72 "hausarzt",
73 "facharzt",
74 ]
76 def __init__(
77 self,
78 patterns: Optional[List[Pattern]] = None,
79 context: Optional[List[str]] = None,
80 supported_language: str = "de",
81 supported_entity: str = "DE_LANR",
82 name: Optional[str] = None,
83 ):
84 patterns = patterns if patterns else self.PATTERNS
85 context = context if context else self.CONTEXT
86 super().__init__(
87 supported_entity=supported_entity,
88 patterns=patterns,
89 context=context,
90 supported_language=supported_language,
91 name=name,
92 )
94 def validate_result(self, pattern_text: str) -> Optional[bool]:
95 """
96 Validate the LANR using the KBV check digit algorithm.
98 Algorithm source: KBV-Richtlinie nach § 75 Abs. 7 SGB V.
100 :param pattern_text: the text to validate (9 digits)
101 :return: True if check digit is valid, False otherwise
102 """
103 pattern_text = pattern_text.strip()
105 if len(pattern_text) != 9 or not pattern_text.isdigit():
106 return False
108 weights = [4, 9, 2, 10, 5, 3]
109 total = 0
110 for digit_char, weight in zip(pattern_text[:6], weights):
111 product = int(digit_char) * weight
112 if product > 9:
113 product = (product // 10) + (product % 10)
114 total += product
116 expected_check = total % 10
117 return int(pattern_text[6]) == expected_check