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
« prev ^ index » next coverage.py v7.13.1, created at 2026-03-29 09:03 +0000
1from typing import List, Optional, Tuple
3from presidio_analyzer import EntityRecognizer, Pattern, PatternRecognizer
6class InVehicleRegistrationRecognizer(PatternRecognizer):
7 """
8 Recognizes Indian Vehicle Registration Number issued by RTO.
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
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
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 """
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 ]
77 CONTEXT = ["RTO", "vehicle", "plate", "registration"]
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 }
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"}
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 }
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
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 )
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)
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 = ""
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]
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
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
395 return is_valid_registration