Coverage for presidio_analyzer / predefined_recognizers / country_specific / korea / kr_brn_recognizer.py: 93%
28 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, Tuple, Union
3from presidio_analyzer import EntityRecognizer, Pattern, PatternRecognizer
6class KrBrnRecognizer(PatternRecognizer):
7 """
8 Recognize Korean Business Registration Number (BRN).
10 The Korean Business Registration Number (BRN) is a 10-digit number
11 assigned to businesses in South Korea for taxation purposes.
13 The format is AAA-BB-CCCCC where:
14 - AAA: District office code
15 - BB: Type of business code
16 - CCCCC: Serial number and check digit
18 Reference: https://org-id.guide/list/KR-BRN
20 :param patterns: List of patterns to be used by this recognizer
21 :param context: List of context words to increase confidence in detection
22 :param supported_language: Language this recognizer supports
23 :param supported_entity: The entity this recognizer can detect
24 :param replacement_pairs: List of tuples with potential replacement values
25 to be used during pattern matching (e.g., removing dashes).
26 """
28 PATTERNS = [
29 Pattern(
30 "BRN (Weak)",
31 r"(?<!\d)\d{3}-\d{2}-\d{5}(?!\d)",
32 0.1,
33 ),
34 Pattern(
35 "BRN (Very weak)",
36 r"(?<!\d)\d{10}(?!\d)",
37 0.05,
38 ),
39 ]
41 CONTEXT = [
42 "사업자등록번호",
43 "사업자번호",
44 "사업자",
45 "BRN",
46 "Business Registration Number",
47 "Korean BRN",
48 "business number",
49 "tax registration number",
50 ]
52 def __init__(
53 self,
54 patterns: Optional[List[Pattern]] = None,
55 context: Optional[List[str]] = None,
56 supported_language: str = "ko",
57 supported_entity: str = "KR_BRN",
58 replacement_pairs: Optional[List[Tuple[str, str]]] = None,
59 ):
60 self.replacement_pairs = replacement_pairs if replacement_pairs else [("-", "")]
62 patterns = patterns if patterns else self.PATTERNS
63 context = context if context else self.CONTEXT
64 super().__init__(
65 supported_entity=supported_entity,
66 patterns=patterns,
67 context=context,
68 supported_language=supported_language,
69 )
71 def validate_result(self, pattern_text: str) -> Union[bool, None]:
72 """
73 Validate the pattern logic by running a checksum on a detected BRN.
75 :param pattern_text: The text detected by the regex engine.
76 :return: True if validation is successful, False otherwise.
77 """
78 # Sanitize the value by removing dashes
79 sanitized_value = EntityRecognizer.sanitize_value(
80 pattern_text, self.replacement_pairs
81 )
83 # BRN must be exactly 10 digits
84 if len(sanitized_value) != 10:
85 return False
87 if not sanitized_value.isdigit():
88 return False
90 return self._validate_checksum(sanitized_value)
92 def _validate_checksum(self, brn: str) -> bool:
93 """
94 Validate the checksum of Korean Business Registration Number.
96 The validation algorithm:
97 1. Multiply the first 9 digits by the magic keys: [1, 3, 7, 1, 3, 7, 1, 3, 5].
98 2. Sum the results of the first 8 multiplications.
99 3. For the 9th digit (index 8):
100 Add the product (digit * 5) AND the quotient of (digit * 5) / 10 to the sum.
101 4. Take the remainder of the total sum divided by 10.
102 5. Subtract the remainder from 10.
103 The result (mod 10) should match the 10th digit.
105 :param brn: The 10-digit BRN string to validate.
106 :return: True if checksum is valid, False otherwise.
107 """
108 digits = [int(d) for d in brn]
109 magic_keys = [1, 3, 7, 1, 3, 7, 1, 3, 5]
111 # Step 1 & 2: Calculate sum for the first 8 digits
112 total_sum = 0
113 for i in range(8):
114 total_sum += digits[i] * magic_keys[i]
116 # Step 3: Special handling for the 9th digit
117 last_key_mul = digits[8] * magic_keys[8]
118 total_sum += (last_key_mul // 10) + last_key_mul
120 # Step 4 & 5: Validate against the 10th check digit
121 remainder = total_sum % 10
122 check_digit = (10 - remainder) % 10
124 return check_digit == digits[9]