Coverage for presidio_analyzer / predefined_recognizers / country_specific / india / in_vehicle_registration_recognizer.py: 97%

87 statements  

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

1from typing import List, Optional, Tuple 

2 

3from presidio_analyzer import EntityRecognizer, Pattern, PatternRecognizer 

4 

5 

6class InVehicleRegistrationRecognizer(PatternRecognizer): 

7 """ 

8 Recognizes Indian Vehicle Registration Number issued by RTO. 

9 

10 Reference(s): 

11 https://en.wikipedia.org/wiki/Vehicle_registration_plates_of_India 

12 https://en.wikipedia.org/wiki/Regional_Transport_Office 

13 https://en.wikipedia.org/wiki/List_of_Regional_Transport_Office_districts_in_India 

14 

15 The registration scheme changed over time with multiple formats 

16 in play over the years 

17 India has multiple active patterns for registration plates issued to different 

18 vehicle categories 

19 

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 for different strings to be used during pattern matching. 

26 This can allow a greater variety in input e.g. by removing dashes or spaces 

27 """ 

28 

29 PATTERNS = [ 

30 Pattern( 

31 "India Vehicle Registration (Very Weak)", 

32 r"\b[A-Z]{1}(?!0000)[0-9]{4}\b", 

33 0.01, 

34 ), 

35 Pattern( 

36 "India Vehicle Registration (Very Weak)", 

37 r"\b[A-Z]{2}(?!0000)\d{4}\b", 

38 0.01, 

39 ), 

40 Pattern( 

41 "India Vehicle Registration (Very Weak)", 

42 r"\b(I)(?!00000)\d{5}\b", 

43 0.01, 

44 ), 

45 Pattern( 

46 "India Vehicle Registration (Weak)", 

47 r"\b[A-Z]{3}(?!0000)\d{4}\b", 

48 0.20, 

49 ), 

50 Pattern( 

51 "India Vehicle Registration (Medium)", 

52 r"\b\d{1,3}(CD|CC|UN)[1-9]{1}[0-9]{1,3}\b", 

53 0.40, 

54 ), 

55 Pattern( 

56 "India Vehicle Registration", 

57 r"\b[A-Z]{2}\d{1}[A-Z]{1,3}(?!0000)\d{4}\b", 

58 0.50, 

59 ), 

60 Pattern( 

61 "India Vehicle Registration", 

62 r"\b[A-Z]{2}\d{2}[A-Z]{1,2}(?!0000)\d{4}\b", 

63 0.50, 

64 ), 

65 Pattern( 

66 "India Vehicle Registration", 

67 r"\b[2-9]{1}[1-9]{1}(BH)(?!0000)\d{4}[A-HJ-NP-Z]{2}\b", 

68 0.85, 

69 ), 

70 Pattern( 

71 "India Vehicle Registration", 

72 r"\b(?!00)\d{2}(A|B|C|D|E|F|H|K|P|R|X)\d{6}[A-Z]{1}\b", 

73 0.85, 

74 ), 

75 ] 

76 

77 CONTEXT = ["RTO", "vehicle", "plate", "registration"] 

78 

79 # fmt: off 

80 in_vehicle_foreign_mission_codes = { 

81 84, 85, 89, 93, 94, 95, 97, 98, 99, 102, 104, 105, 106, 109, 111, 112, 

82 113, 117, 119, 120, 121, 122, 123, 125, 126, 128, 133, 134, 135, 137, 

83 141, 145, 147, 149, 152, 153, 155, 156, 157, 159, 160 

84 } 

85 

86 in_vehicle_armed_forces_codes = { 

87 'A', 'B', 'C', 'D', 'E', 'F', 'H', 'K', 'P', 'R', 'X'} 

88 in_vehicle_diplomatic_codes = {"CC", "CD", "UN"} 

89 in_vehicle_dist_an = {"01"} 

90 in_vehicle_dist_ap = {"39", "40"} 

91 in_vehicle_dist_ar = { 

92 "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", 

93 "13", "14", "15", "16", "17", "19", "20", "22" 

94 } 

95 in_vehicle_dist_as = { 

96 "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", 

97 "13", "14", "15", "16", "17", "18", "19", "20", "22", "23", "24", "25", 

98 "26", "27", "28", "29", "30", "31", "32", "33", "34" 

99 } 

100 in_vehicle_dist_br = { 

101 "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "19", 

102 "21", "22", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", 

103 "34", "37", "38", "39", "43", "44", "45", "46", "50", "51", "52", "53", 

104 "55", "56" 

105 } 

106 in_vehicle_dist_cg = { 

107 "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", 

108 "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", 

109 "25", "26", "27", "28", "29", "30" 

110 } 

111 in_vehicle_dist_ch = {"01", "02", "03", "04"} 

112 in_vehicle_dist_dd = {"01", "02", "03"} 

113 in_vehicle_dist_dn = {"09"} # old list 

114 in_vehicle_dist_dl = { 

115 "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13"} 

116 in_vehicle_dist_ga = { 

117 "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"} 

118 in_vehicle_dist_gj = { 

119 "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", 

120 "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", 

121 "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", 

122 "38", "39" 

123 } 

124 in_vehicle_dist_hp = { 

125 "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", 

126 "13", "14", "15", "16", "17", "18", "19", "20", "22", "23", "24", "25", 

127 "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", 

128 "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", 

129 "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61", 

130 "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", 

131 "74", "75", "76", "77", "78", "79", "80", "81", "82", "83", "84", "85", 

132 "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97", 

133 "98", "99" 

134 } 

135 in_vehicle_dist_hr = { 

136 "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", 

137 "13", "14", "15", "16", "17", "18", "19", "20", "22", "23", "24", "25", 

138 "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", 

139 "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", 

140 "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61", 

141 "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", 

142 "74", "75", "76", "77", "78", "79", "80", "81", "82", "83", "84", "85", 

143 "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97", 

144 "98", "99" 

145 } 

146 in_vehicle_dist_jh = { 

147 "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", 

148 "13", "14", "15", "16", "17", "18", "19", "20", "22", "23", "24" 

149 } 

150 in_vehicle_dist_jk = { 

151 "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", 

152 "13", "14", "15", "16", "17", "18", "19", "20", "22" 

153 } 

154 in_vehicle_dist_ka = { 

155 "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", 

156 "13", "14", "15", "16", "17", "18", "19", "20", "22", "23", "24", "25", 

157 "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", 

158 "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", 

159 "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61", 

160 "62", "63", "64", "65", "66", "67", "68", "69", "70", "71" 

161 } 

162 in_vehicle_dist_kl = { 

163 "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", 

164 "13", "14", "15", "16", "17", "18", "19", "20", "22", "23", "24", "25", 

165 "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", 

166 "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", 

167 "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61", 

168 "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", 

169 "74", "75", "76", "77", "78", "79", "80", "81", "82", "83", "84", "85", 

170 "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97", 

171 "98", "99" 

172 } 

173 in_vehicle_dist_la = {"01", "02"} 

174 in_vehicle_dist_ld = {"01", "02", "03", "04", "05", "06", "07", "08", "09"} 

175 in_vehicle_dist_mh = { 

176 "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", 

177 "13", "14", "15", "16", "17", "18", "19", "20", "22", "23", "24", "25", 

178 "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", 

179 "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", 

180 "50", "51" 

181 } 

182 in_vehicle_dist_ml = { 

183 "01", "02", "03", "04", "05", "06", "07", "08", "09", "10"} 

184 in_vehicle_dist_mn = {"01", "02", "03", "04", "05", "06", "07"} 

185 in_vehicle_dist_mp = { 

186 "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", 

187 "13", "14", "15", "16", "17", "18", "19", "20", "22", "23", "24", "25", 

188 "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", 

189 "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", 

190 "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61", 

191 "62", "63", "64", "65", "66", "67", "68", "69", "70", "71" 

192 } 

193 in_vehicle_dist_mz = {"01", "02", "03", "04", "05", "06", "07", "08"} 

194 in_vehicle_dist_nl = {"01", "02", "03", "04", "05", "06", "07", "08", "09", "10"} 

195 in_vehicle_dist_od = { 

196 "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", 

197 "13", "14", "15", "16", "17", "18", "19", "20", "22", "23", "24", "25", 

198 "26", "27", "28", "29", "30", "31", "32", "33", "34", "35" 

199 } 

200 in_vehicle_dist_or = { 

201 "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", 

202 "13", "14", "15", "16", "17", "18", "19", "20", "22", "23", "24", "25", 

203 "26", "27", "28", "29", "30", "31" 

204 } # old list 

205 in_vehicle_dist_pb = { 

206 "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", 

207 "13", "14", "15", "16", "17", "18", "19", "20", "22", "23", "24", "25", 

208 "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", 

209 "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", 

210 "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61", 

211 "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", 

212 "74", "75", "76", "77", "78", "79", "80", "81", "82", "83", "84", "85", 

213 "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97", 

214 "98", "99" 

215 } 

216 in_vehicle_dist_py = {"01", "02", "03", "04", "05"} 

217 in_vehicle_dist_rj = { 

218 "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", 

219 "13", "14", "15", "16", "17", "18", "19", "20", "22", "23", "24", "25", 

220 "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", 

221 "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", 

222 "50", "51", "52", "53", "54", "55", "56", "57", "58" 

223 } 

224 in_vehicle_dist_sk = {"01", "02", "03", "04", "05", "06", "07", "08"} 

225 in_vehicle_dist_tn = { 

226 "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", 

227 "13", "14", "15", "16", "17", "18", "19", "20", "22", "23", "24", "25", 

228 "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", 

229 "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", 

230 "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61", 

231 "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", 

232 "74", "75", "76", "77", "78", "79", "80", "81", "82", "83", "84", "85", 

233 "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97", 

234 "98", "99" 

235 } 

236 in_vehicle_dist_tr = {"01", "02", "03", "04", "05", "06", "07", "08"} 

237 in_vehicle_dist_ts = { 

238 "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", 

239 "13", "14", "15", "16", "17", "18", "19", "20", "22", "23", "24", "25", 

240 "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", 

241 "38" 

242 } 

243 in_vehicle_dist_uk = { 

244 "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", 

245 "13", "14", "15", "16", "17", "18", "19", "20" 

246 } 

247 in_vehicle_dist_up = { 

248 "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "22", "23", 

249 "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", 

250 "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", 

251 "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", 

252 "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", 

253 "72", "73", "74", "75", "76", "77", "78", "79", "80", "81", "82", "83", 

254 "84", "85", "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", 

255 "96" 

256 } 

257 in_vehicle_dist_wb = { 

258 "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", 

259 "13", "14", "15", "16", "17", "18", "19", "20", "22", "23", "24", "25", 

260 "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", 

261 "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", 

262 "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61", 

263 "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", 

264 "74", "75", "76", "77", "78", "79", "80", "81", "82", "83", "84", "85", 

265 "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97", 

266 "98" 

267 } 

268 in_union_territories = {"AN", "CH", "DH", "DL", "JK", "LA", "LD", "PY"} 

269 in_old_union_territories = {"CT", "DN"} 

270 in_states = { 

271 "AP", "AR", "AS", "BR", "CG", "GA", "GJ", "HR", "HP", "JH", "KA", "KL", 

272 "MP", "MH", "MN", "ML", "MZ", "NL", "OD", "PB", "RJ", "SK", "TN", "TS", 

273 "TR", "UP", "UK", "WB", "UT" 

274 } 

275 in_old_states = {"UL", "OR", "UA"} 

276 in_non_standard_state_or_ut = {"DD"} 

277 

278 state_rto_district_map = { 

279 "AN": in_vehicle_dist_an, 

280 "AP": in_vehicle_dist_ap, 

281 "AR": in_vehicle_dist_ar, 

282 "AS": in_vehicle_dist_as, 

283 "BR": in_vehicle_dist_br, 

284 "CG": in_vehicle_dist_cg, 

285 "CH": in_vehicle_dist_ch, 

286 "DD": in_vehicle_dist_dd, 

287 "DN": in_vehicle_dist_dn, 

288 "DL": in_vehicle_dist_dl, 

289 "GA": in_vehicle_dist_ga, 

290 "GJ": in_vehicle_dist_gj, 

291 "HP": in_vehicle_dist_hp, 

292 "HR": in_vehicle_dist_hr, 

293 "JH": in_vehicle_dist_jh, 

294 "JK": in_vehicle_dist_jk, 

295 "KA": in_vehicle_dist_ka, 

296 "KL": in_vehicle_dist_kl, 

297 "LA": in_vehicle_dist_la, 

298 "LD": in_vehicle_dist_ld, 

299 "MH": in_vehicle_dist_mh, 

300 "ML": in_vehicle_dist_ml, 

301 "MN": in_vehicle_dist_mn, 

302 "MP": in_vehicle_dist_mp, 

303 "MZ": in_vehicle_dist_mz, 

304 "NL": in_vehicle_dist_nl, 

305 "OD": in_vehicle_dist_od, 

306 "OR": in_vehicle_dist_or, 

307 "PB": in_vehicle_dist_pb, 

308 "PY": in_vehicle_dist_py, 

309 "RJ": in_vehicle_dist_rj, 

310 "SK": in_vehicle_dist_sk, 

311 "TN": in_vehicle_dist_tn, 

312 "TR": in_vehicle_dist_tr, 

313 "TS": in_vehicle_dist_ts, 

314 "UK": in_vehicle_dist_uk, 

315 "UP": in_vehicle_dist_up, 

316 "WB": in_vehicle_dist_wb, 

317 } 

318 

319 two_factor_registration_prefix = set() 

320 two_factor_registration_prefix |= in_union_territories 

321 two_factor_registration_prefix |= in_states 

322 two_factor_registration_prefix |= in_old_states 

323 two_factor_registration_prefix |= in_old_union_territories 

324 two_factor_registration_prefix |= in_non_standard_state_or_ut 

325 # fmt: on 

326 

327 def __init__( 

328 self, 

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

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

331 supported_language: str = "en", 

332 supported_entity: str = "IN_VEHICLE_REGISTRATION", 

333 replacement_pairs: Optional[List[Tuple[str, str]]] = None, 

334 name: Optional[str] = None, 

335 ): 

336 self.replacement_pairs = ( 

337 replacement_pairs 

338 if replacement_pairs 

339 else [("-", ""), (" ", ""), (":", "")] 

340 ) 

341 patterns = patterns if patterns else self.PATTERNS 

342 context = context if context else self.CONTEXT 

343 super().__init__( 

344 supported_entity=supported_entity, 

345 patterns=patterns, 

346 context=context, 

347 supported_language=supported_language, 

348 name=name, 

349 ) 

350 

351 def validate_result(self, pattern_text: str) -> bool: 

352 """Determine absolute value based on calculation.""" 

353 sanitized_value = EntityRecognizer.sanitize_value( 

354 pattern_text, self.replacement_pairs 

355 ) 

356 return self.__check_vehicle_registration(sanitized_value) 

357 

358 def __check_vehicle_registration(self, sanitized_value: str) -> bool: 

359 # print('check function called') 

360 is_valid_registration = None # deliberately not typecasted or set to bool False 

361 # logic here 

362 if len(sanitized_value) >= 8: 

363 first_two_char = sanitized_value[:2].upper() 

364 dist_code: str = "" 

365 

366 if first_two_char in self.two_factor_registration_prefix: 

367 if sanitized_value[2].isdigit(): 

368 if sanitized_value[3].isdigit(): 

369 dist_code = sanitized_value[2:4] 

370 else: 

371 dist_code = sanitized_value[2:3] 

372 

373 registration_digits = sanitized_value[-4:] 

374 if registration_digits.isnumeric(): 

375 if 0 < int(registration_digits) <= 9999: 

376 if ( 

377 dist_code 

378 and dist_code 

379 in self.state_rto_district_map.get(first_two_char, "") 

380 ): 

381 is_valid_registration = True 

382 

383 for diplomatic_vehicle_code in self.in_vehicle_diplomatic_codes: 

384 if diplomatic_vehicle_code in sanitized_value: 

385 vehicle_prefix = sanitized_value.partition( 

386 diplomatic_vehicle_code 

387 )[0] 

388 if vehicle_prefix.isnumeric() and ( 

389 1 <= int(vehicle_prefix) <= 80 

390 or int(vehicle_prefix) 

391 in self.in_vehicle_foreign_mission_codes 

392 ): 

393 is_valid_registration = True 

394 

395 return is_valid_registration