Coverage for presidio_analyzer / predefined_recognizers / country_specific / us / us_mbi_recognizer.py: 100%

16 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-03-29 09:03 +0000

1"""Recognizer for US Medicare Beneficiary Identifier (MBI).""" 

2 

3from typing import List, Optional 

4 

5from presidio_analyzer import Pattern, PatternRecognizer 

6 

7 

8class UsMbiRecognizer(PatternRecognizer): 

9 """Recognize US Medicare Beneficiary Identifier (MBI) using regex. 

10 

11 The MBI is an 11-character identifier used by Medicare. The format follows 

12 specific rules defined by CMS (Centers for Medicare & Medicaid Services): 

13 https://www.cms.gov/medicare/new-medicare-card/understanding-new-medicare-beneficiary-identifier-mbi 

14 

15 Format: C A AN N A AN N A A N N 

16 Where: 

17 - C = numeric character (0-9) 

18 - A = alphabetic character (excluding S, L, O, I, B, Z) 

19 - AN = alphanumeric character (numeric or alphabetic, excluding S, L, O, I, B, Z) 

20 

21 Position rules: 

22 - Positions 1, 4, 7, 10, 11: numeric (0-9) 

23 - Positions 2, 5, 8, 9: alphabetic 

24 - Positions 3, 6: alphanumeric 

25 

26 Example: 1EG4-TE5-MK73 (dashes are for display only) 

27 

28 :param patterns: List of patterns to be used by this recognizer 

29 :param context: List of context words to increase confidence in detection 

30 :param supported_language: Language this recognizer supports 

31 :param supported_entity: The entity this recognizer can detect 

32 """ 

33 

34 # Valid letters: A-Z excluding S, L, O, I, B, Z 

35 # Valid letters are: A, C, D, E, F, G, H, J, K, M, N, P, Q, R, T, U, V, W, X, Y 

36 VALID_LETTERS = "ACDEFGHJKMNPQRTUVWXY" 

37 VALID_ALPHANUMERIC = "0-9ACDEFGHJKMNPQRTUVWXY" 

38 

39 # Regex building blocks 

40 _NUM = "[0-9]" 

41 _ALPHA = f"[{VALID_LETTERS}]" 

42 _ALPHANUM = f"[{VALID_ALPHANUMERIC}]" 

43 

44 # Full MBI pattern: 

45 # Pos: 1 2 3 4 5 6 7 8 9 10 11 

46 # NUM ALPHA ALPHANUM NUM ALPHA ALPHANUM NUM ALPHA ALPHA NUM NUM 

47 

48 # Pattern without dashes (11 consecutive characters) 

49 _MBI_NO_DASH = ( 

50 f"{_NUM}{_ALPHA}{_ALPHANUM}{_NUM}" 

51 f"{_ALPHA}{_ALPHANUM}{_NUM}" 

52 f"{_ALPHA}{_ALPHA}{_NUM}{_NUM}" 

53 ) 

54 

55 # Pattern with dashes in XXXX-XXX-XXXX format 

56 _MBI_WITH_DASH = ( 

57 f"{_NUM}{_ALPHA}{_ALPHANUM}{_NUM}-" 

58 f"{_ALPHA}{_ALPHANUM}{_NUM}-" 

59 f"{_ALPHA}{_ALPHA}{_NUM}{_NUM}" 

60 ) 

61 

62 PATTERNS = [ 

63 Pattern( 

64 "MBI (weak)", 

65 rf"\b{_MBI_NO_DASH}\b", 

66 0.3, 

67 ), 

68 Pattern( 

69 "MBI (medium)", 

70 rf"\b{_MBI_WITH_DASH}\b", 

71 0.5, 

72 ), 

73 ] 

74 

75 CONTEXT = [ 

76 "medicare", 

77 "mbi", 

78 "beneficiary", 

79 "cms", 

80 "medicaid", 

81 "hic", # Health Insurance Claim number (predecessor) 

82 "hicn", 

83 ] 

84 

85 def __init__( 

86 self, 

87 patterns: Optional[List[Pattern]] = None, 

88 context: Optional[List[str]] = None, 

89 supported_language: str = "en", 

90 supported_entity: str = "US_MBI", 

91 ): 

92 patterns = patterns if patterns else self.PATTERNS 

93 context = context if context else self.CONTEXT 

94 super().__init__( 

95 supported_entity=supported_entity, 

96 patterns=patterns, 

97 context=context, 

98 supported_language=supported_language, 

99 )