Coverage for presidio_analyzer / predefined_recognizers / country_specific / italy / it_fiscal_code_recognizer.py: 100%

29 statements  

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

1from typing import List, Optional 

2 

3from presidio_analyzer import Pattern, PatternRecognizer 

4 

5 

6class ItFiscalCodeRecognizer(PatternRecognizer): 

7 """ 

8 Recognizes IT Fiscal Code using regex. 

9 

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

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

12 :param supported_language: Language this recognizer supports 

13 :param supported_entity: The entity this recognizer can detect 

14 """ 

15 

16 PATTERNS = [ 

17 Pattern( 

18 "Fiscal Code", 

19 ( 

20 r"(?i)((?:[A-Z][AEIOU][AEIOUX]|[AEIOU]X{2}" 

21 r"|[B-DF-HJ-NP-TV-Z]{2}[A-Z]){2}" 

22 r"(?:[\dLMNP-V]{2}(?:[A-EHLMPR-T](?:[04LQ][1-9MNP-V]|[15MR][\dLMNP-V]" 

23 r"|[26NS][0-8LMNP-U])|[DHPS][37PT][0L]|[ACELMRT][37PT][01LM]" 

24 r"|[AC-EHLMPR-T][26NS][9V])|(?:[02468LNQSU][048LQU]" 

25 r"|[13579MPRTV][26NS])B[26NS][9V])(?:[A-MZ][1-9MNP-V][\dLMNP-V]{2}" 

26 r"|[A-M][0L](?:[1-9MNP-V][\dLMNP-V]|[0L][1-9MNP-V]))[A-Z])" 

27 ), 

28 0.3, 

29 ), 

30 ] 

31 CONTEXT = ["codice fiscale", "cf"] 

32 

33 def __init__( 

34 self, 

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

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

37 supported_language: str = "it", 

38 supported_entity: str = "IT_FISCAL_CODE", 

39 name: Optional[str] = None, 

40 ): 

41 patterns = patterns if patterns else self.PATTERNS 

42 context = context if context else self.CONTEXT 

43 super().__init__( 

44 supported_entity=supported_entity, 

45 patterns=patterns, 

46 context=context, 

47 supported_language=supported_language, 

48 name=name, 

49 ) 

50 

51 def validate_result(self, pattern_text: str) -> Optional[bool]: 

52 """ 

53 Validate the pattern logic e.g., by running checksum on a detected pattern. 

54 

55 :param pattern_text: the text to validated. 

56 Only the part in text that was detected by the regex engine 

57 :return: A bool indicating whether the validation was successful. 

58 """ 

59 pattern_text = pattern_text.upper() 

60 control = pattern_text[-1] 

61 text_to_validate = pattern_text[:-1] 

62 odd_values = text_to_validate[0::2] 

63 even_values = text_to_validate[1::2] 

64 

65 # Odd values 

66 map_odd = { 

67 "0": 1, 

68 "1": 0, 

69 "2": 5, 

70 "3": 7, 

71 "4": 9, 

72 "5": 13, 

73 "6": 15, 

74 "7": 17, 

75 "8": 19, 

76 "9": 21, 

77 "A": 1, 

78 "B": 0, 

79 "C": 5, 

80 "D": 7, 

81 "E": 9, 

82 "F": 13, 

83 "G": 15, 

84 "H": 17, 

85 "I": 19, 

86 "J": 21, 

87 "K": 2, 

88 "L": 4, 

89 "M": 18, 

90 "N": 20, 

91 "O": 11, 

92 "P": 3, 

93 "Q": 6, 

94 "R": 8, 

95 "S": 12, 

96 "T": 14, 

97 "U": 16, 

98 "V": 10, 

99 "W": 22, 

100 "X": 25, 

101 "Y": 24, 

102 "Z": 23, 

103 } 

104 

105 odd_sum = 0 

106 for char in odd_values: 

107 odd_sum += map_odd[char] 

108 

109 # Even values 

110 map_even = { 

111 "0": 0, 

112 "1": 1, 

113 "2": 2, 

114 "3": 3, 

115 "4": 4, 

116 "5": 5, 

117 "6": 6, 

118 "7": 7, 

119 "8": 8, 

120 "9": 9, 

121 "A": 0, 

122 "B": 1, 

123 "C": 2, 

124 "D": 3, 

125 "E": 4, 

126 "F": 5, 

127 "G": 6, 

128 "H": 7, 

129 "I": 8, 

130 "J": 9, 

131 "K": 10, 

132 "L": 11, 

133 "M": 12, 

134 "N": 13, 

135 "O": 14, 

136 "P": 15, 

137 "Q": 16, 

138 "R": 17, 

139 "S": 18, 

140 "T": 19, 

141 "U": 20, 

142 "V": 21, 

143 "W": 22, 

144 "X": 23, 

145 "Y": 24, 

146 "Z": 25, 

147 } 

148 

149 even_sum = 0 

150 for char in even_values: 

151 even_sum += map_even[char] 

152 

153 # Mod value 

154 map_mod = { 

155 0: "A", 

156 1: "B", 

157 2: "C", 

158 3: "D", 

159 4: "E", 

160 5: "F", 

161 6: "G", 

162 7: "H", 

163 8: "I", 

164 9: "J", 

165 10: "K", 

166 11: "L", 

167 12: "M", 

168 13: "N", 

169 14: "O", 

170 15: "P", 

171 16: "Q", 

172 17: "R", 

173 18: "S", 

174 19: "T", 

175 20: "U", 

176 21: "V", 

177 22: "W", 

178 23: "X", 

179 24: "Y", 

180 25: "Z", 

181 } 

182 check_value = map_mod[((odd_sum + even_sum) % 26)] 

183 

184 if check_value == control: 

185 result = True 

186 else: 

187 result = None 

188 

189 return result