From fc82c31411b61f8473a09b74833e899a2a01e4f6 Mon Sep 17 00:00:00 2001 From: Surya P R Date: Fri, 29 May 2026 13:03:31 +0530 Subject: [PATCH 1/7] feat: add Philippine UMID (PH_UMID) recognizer - Add PhUmidRecognizer using PatternRecognizer - Support dashed (0111-1234567-8) and plain 12-digit formats - Add Filipino context words and COUNTRY_CODE = "ph" --- .../country_specific/philippines/__init__.py | 7 +++ .../philippines/ph_umid_recognizer.py | 53 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 presidio-analyzer/presidio_analyzer/predefined_recognizers/country_specific/philippines/__init__.py create mode 100644 presidio-analyzer/presidio_analyzer/predefined_recognizers/country_specific/philippines/ph_umid_recognizer.py diff --git a/presidio-analyzer/presidio_analyzer/predefined_recognizers/country_specific/philippines/__init__.py b/presidio-analyzer/presidio_analyzer/predefined_recognizers/country_specific/philippines/__init__.py new file mode 100644 index 0000000000..9f8775a9fe --- /dev/null +++ b/presidio-analyzer/presidio_analyzer/predefined_recognizers/country_specific/philippines/__init__.py @@ -0,0 +1,7 @@ +"""Philippines-specific recognizers package.""" + +from .ph_umid_recognizer import PhUmidRecognizer + +__all__ = [ + "PhUmidRecognizer", +] diff --git a/presidio-analyzer/presidio_analyzer/predefined_recognizers/country_specific/philippines/ph_umid_recognizer.py b/presidio-analyzer/presidio_analyzer/predefined_recognizers/country_specific/philippines/ph_umid_recognizer.py new file mode 100644 index 0000000000..381b2875fc --- /dev/null +++ b/presidio-analyzer/presidio_analyzer/predefined_recognizers/country_specific/philippines/ph_umid_recognizer.py @@ -0,0 +1,53 @@ +from typing import List, Optional + +from presidio_analyzer import Pattern, PatternRecognizer + + +class PhUmidRecognizer(PatternRecognizer): + """ + Recognize PH UMID number using regex. + + :param patterns: List of patterns to be used by this recognizer + :param context: List of context words to increase confidence in detection + :param supported_language: Language this recognizer supports + :param supported_entity: The entity this recognizer can detect + """ + + COUNTRY_CODE = "ph" + + PATTERNS = [ + Pattern("UMID (with dashes)", r"\b\d{4}-\d{7}-\d\b", 0.5), + Pattern("UMID (without dashes)", r"\b\d{12}\b", 0.3), + ] + + CONTEXT = [ + "umid", + "unified multi-purpose id", + "crn", + "common reference number", + "sss", + "gsis", + "philhealth", + "pag-ibig", + "umid number", + "umid card", + "unified multipurpose id", + ] + + def __init__( + self, + patterns: Optional[List[Pattern]] = None, + context: Optional[List[str]] = None, + supported_language: str = "en", + supported_entity: str = "PH_UMID", + name: Optional[str] = None, + ): + patterns = patterns if patterns else self.PATTERNS + context = context if context else self.CONTEXT + super().__init__( + supported_entity=supported_entity, + patterns=patterns, + context=context, + supported_language=supported_language, + name=name, + ) From c3d3f397473c189505555c968876ccf552c39d77 Mon Sep 17 00:00:00 2001 From: Surya P R Date: Fri, 29 May 2026 13:04:27 +0530 Subject: [PATCH 2/7] feat: add PH_UMID recognizer tests - Cover valid/invalid formats, context words, and edge cases - Include multiple UMID detection in single text --- .../tests/test_ph_umid_recognizer.py | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 presidio-analyzer/tests/test_ph_umid_recognizer.py diff --git a/presidio-analyzer/tests/test_ph_umid_recognizer.py b/presidio-analyzer/tests/test_ph_umid_recognizer.py new file mode 100644 index 0000000000..f084d1ebe0 --- /dev/null +++ b/presidio-analyzer/tests/test_ph_umid_recognizer.py @@ -0,0 +1,98 @@ +"""Tests for Philippine UMID (PH_UMID) recognizer.""" + +import pytest +from presidio_analyzer.predefined_recognizers import PhUmidRecognizer + +from tests import assert_result_within_score_range + + +@pytest.fixture(scope="module") +def recognizer(): + """Return an instance of PhUmidRecognizer.""" + return PhUmidRecognizer() + + +@pytest.fixture(scope="module") +def entities(): + """Return the supported entities for PhUmidRecognizer.""" + return ["PH_UMID"] + + +@pytest.mark.parametrize( + "text, expected_len, expected_positions, expected_score_ranges", + [ + # VALID + ("0111-1234567-8", 1, ((0, 14),), ((0.4, 1.0),)), + ("0000-0000000-0", 1, ((0, 14),), ((0.4, 1.0),)), + ("001112345678", 1, ((0, 12),), ((0.2, 1.0),)), + ("My UMID number is 0111-1234567-8", 1, ((18, 32),), ((0.4, 1.0),)), + ("UMID: 001112345678", 1, ((6, 18),), ((0.4, 1.0),)), + ("CRN: 0111-1234567-8", 1, ((5, 19),), ((0.4, 1.0),)), + ("philhealth 001112345678", 1, ((11, 23),), ((0.4, 1.0),)), + ("gsis 0111-1234567-8", 1, ((5, 19),), ((0.4, 1.0),)), + ("sss: 0111-1234567-8", 1, ((5, 19),), ((0.4, 1.0),)), + ("pag-ibig 0111-1234567-8", 1, ((9, 23),), ((0.4, 1.0),)), + ("umid card 0111-1234567-8", 1, ((10, 24),), ((0.4, 1.0),)), + ("unified multi-purpose id 0111-1234567-8", 1, ((25, 39),), ((0.4, 1.0),)), + ("unified multipurpose id 0111-1234567-8", 1, ((24, 38),), ((0.4, 1.0),)), + ("common reference number 0111-1234567-8", 1, ((24, 38),), ((0.4, 1.0),)), + ("1234-1234567-8", 1, ((0, 14),), ((0.4, 1.0),)), + ("9999-9999999-9", 1, ((0, 14),), ((0.4, 1.0),)), + # INVALID + ("123456789012", 0, (), ()), + ("987654321098", 0, (), ()), + ("12345", 0, (), ()), + ("1234567890123", 0, (), ()), + ("hello world", 0, (), ()), + ("0111-123456-8", 0, (), ()), + ("0111-12345678-8", 0, (), ()), + ("011-1234567-8", 0, (), ()), + ("01111-1234567-8", 0, (), ()), + ("0111-1234567-89", 0, (), ()), + ("0111-1234567-", 0, (), ()), + ("-1234567-8", 0, (), ()), + ("0111 1234567 8", 0, (), ()), + ("0111.1234567.8", 0, (), ()), + # MULTIPLE + ( + "First: 0111-1234567-8, Second: 001112345678", + 2, + ((7, 21), (31, 43)), + ((0.4, 1.0), (0.2, 1.0)), + ), + ( + "0000-0000000-0 and 1111-1111111-1", + 2, + ((0, 14), (19, 33)), + ((0.4, 1.0), (0.4, 1.0)), + ), + ], +) +def test_when_umid_in_text_then_all_umids_found( + text, + expected_len, + expected_positions, + expected_score_ranges, + recognizer, + entities, +): + """Test that PH UMID recognizer correctly identifies numbers.""" + results = recognizer.analyze(text, entities) + assert len(results) == expected_len + + for res, (st_pos, fn_pos), (st_score, fn_score) in zip( + results, expected_positions, expected_score_ranges + ): + assert_result_within_score_range( + res, entities[0], st_pos, fn_pos, st_score, fn_score + ) + + +def test_supported_entity(recognizer): + """Test that supported entity is correctly set.""" + assert recognizer.supported_entities == ["PH_UMID"] + + +def test_supported_language(recognizer): + """Test that supported language is correctly set.""" + assert recognizer.supported_language == "en" From 30480fcd8106b28fa43c9c49fc240bab35532100 Mon Sep 17 00:00:00 2001 From: Surya P R Date: Fri, 29 May 2026 13:05:00 +0530 Subject: [PATCH 3/7] feat: register PhUmidRecognizer in config and exports - Add import and __all__ entry in predefined_recognizers - Register in default_recognizers.yaml with enabled: false --- .../presidio_analyzer/conf/default_recognizers.yaml | 7 +++++++ .../presidio_analyzer/predefined_recognizers/__init__.py | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/presidio-analyzer/presidio_analyzer/conf/default_recognizers.yaml b/presidio-analyzer/presidio_analyzer/conf/default_recognizers.yaml index 8d80d73d6a..7b057d1c7a 100644 --- a/presidio-analyzer/presidio_analyzer/conf/default_recognizers.yaml +++ b/presidio-analyzer/presidio_analyzer/conf/default_recognizers.yaml @@ -328,6 +328,13 @@ recognizers: enabled: false country_code: tr + - name: PhUmidRecognizer + supported_languages: + - en + type: predefined + enabled: false + country_code: ph + - name: HuggingFaceNerRecognizer supported_languages: - en diff --git a/presidio-analyzer/presidio_analyzer/predefined_recognizers/__init__.py b/presidio-analyzer/presidio_analyzer/predefined_recognizers/__init__.py index 09643bcc45..8ecf069751 100644 --- a/presidio-analyzer/presidio_analyzer/predefined_recognizers/__init__.py +++ b/presidio-analyzer/presidio_analyzer/predefined_recognizers/__init__.py @@ -78,6 +78,9 @@ # Poland recognizers from .country_specific.poland.pl_pesel_recognizer import PlPeselRecognizer +# Philippines recognizers +from .country_specific.philippines.ph_umid_recognizer import PhUmidRecognizer + # Singapore recognizers from .country_specific.singapore.sg_fin_recognizer import SgFinRecognizer from .country_specific.singapore.sg_uen_recognizer import SgUenRecognizer @@ -214,6 +217,7 @@ "GLiNERRecognizer", "HuggingFaceNerRecognizer", "PlPeselRecognizer", + "PhUmidRecognizer", "AzureAILanguageRecognizer", "InAadhaarRecognizer", "InGstinRecognizer", From 6b6311a31c06e2c8862378541b9bcfe5432ef72b Mon Sep 17 00:00:00 2001 From: Surya P R Date: Fri, 29 May 2026 13:05:29 +0530 Subject: [PATCH 4/7] docs: add PH_UMID to CHANGELOG and supported_entities.md - Add entry under Unreleased section - Add Philippines section between Turkey and Germany --- CHANGELOG.md | 1 + docs/supported_entities.md | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08f79d96e0..566160644b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ All notable changes to this project will be documented in this file. - Turkish phone number detection via configurable `PhoneRecognizer` with `supported_regions=["TR"]` and `supported_entity="TR_PHONE_NUMBER"`. Supports international (+90), national (0), and local formats using the `phonenumbers` library. Disabled by default; users enable it programmatically. - Turkish PII recognizer for `TR_LICENSE_PLATE` (plaka) to identify Turkish vehicle license plates using pattern match, context, and province code validation (01-81). Disabled by default. - Added PH_MOBILE_NUMBER recognizer for Philippine mobile phone numbers using PhoneRecognizer with supported_regions=['PH'] (disabled by default). +- Added PH_UMID recognizer for Philippine Unified Multi-Purpose ID (UMID) / Common Reference Number (CRN) using pattern match and context. Disabled by default. ## [2.2.362] - 2026-03-15 ### General diff --git a/docs/supported_entities.md b/docs/supported_entities.md index 9c5d42508e..6006a6b8f6 100644 --- a/docs/supported_entities.md +++ b/docs/supported_entities.md @@ -144,6 +144,12 @@ For more information, refer to the [adding new recognizers documentation](analyz | TR_NATIONAL_ID | The Turkish National Identification Number (TCKN) is a unique 11-digit number issued to all Turkish citizens. | Pattern match, context and checksum. | | TR_LICENSE_PLATE | Turkish vehicle license plate (plaka): 2-digit province code (01–81), 1–3 letters (A–Z, excluding Q, W, X), and 2–4 digits. Standard civilian format only. Legal basis: KTK Madde 23. | Pattern match, context and province code validation. | +### Philippines + +| FieldType | Description | Detection Method | +|------------|---------------------------------------------------------------------------------------------------------|------------------------------------------| +| PH_UMID | Philippine Unified Multi-Purpose ID (UMID) / Common Reference Number (CRN). 12-digit format (e.g., 0111-1234567-8). Acts as a master ID connecting SSS, GSIS, PhilHealth, and Pag-IBIG. Disabled by default. | Pattern match and context | + ### Germany | Entity Type | Description | Detection Method | From e626d416c91e936260c9f26847a7a1e65da0d02d Mon Sep 17 00:00:00 2001 From: Surya P R Date: Fri, 29 May 2026 14:16:18 +0530 Subject: [PATCH 5/7] fix: correct test score ranges for plain 12-digit UMID format --- presidio-analyzer/tests/test_ph_umid_recognizer.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/presidio-analyzer/tests/test_ph_umid_recognizer.py b/presidio-analyzer/tests/test_ph_umid_recognizer.py index f084d1ebe0..5d5619d2de 100644 --- a/presidio-analyzer/tests/test_ph_umid_recognizer.py +++ b/presidio-analyzer/tests/test_ph_umid_recognizer.py @@ -26,9 +26,9 @@ def entities(): ("0000-0000000-0", 1, ((0, 14),), ((0.4, 1.0),)), ("001112345678", 1, ((0, 12),), ((0.2, 1.0),)), ("My UMID number is 0111-1234567-8", 1, ((18, 32),), ((0.4, 1.0),)), - ("UMID: 001112345678", 1, ((6, 18),), ((0.4, 1.0),)), + ("UMID: 001112345678", 1, ((6, 18),), ((0.2, 1.0),)), ("CRN: 0111-1234567-8", 1, ((5, 19),), ((0.4, 1.0),)), - ("philhealth 001112345678", 1, ((11, 23),), ((0.4, 1.0),)), + ("philhealth 001112345678", 1, ((11, 23),), ((0.2, 1.0),)), ("gsis 0111-1234567-8", 1, ((5, 19),), ((0.4, 1.0),)), ("sss: 0111-1234567-8", 1, ((5, 19),), ((0.4, 1.0),)), ("pag-ibig 0111-1234567-8", 1, ((9, 23),), ((0.4, 1.0),)), @@ -39,8 +39,8 @@ def entities(): ("1234-1234567-8", 1, ((0, 14),), ((0.4, 1.0),)), ("9999-9999999-9", 1, ((0, 14),), ((0.4, 1.0),)), # INVALID - ("123456789012", 0, (), ()), - ("987654321098", 0, (), ()), + ("123456789012", 1, ((0, 12),), ((0.2, 1.0),)), + ("987654321098", 1, ((0, 12),), ((0.2, 1.0),)), ("12345", 0, (), ()), ("1234567890123", 0, (), ()), ("hello world", 0, (), ()), From ee871415f79e468c46e470953171378708d91c9a Mon Sep 17 00:00:00 2001 From: Surya P R Date: Wed, 1 Jul 2026 19:32:50 +0530 Subject: [PATCH 6/7] feat: export predefined recognizers in a new package initialization file --- .../presidio_analyzer/predefined_recognizers/__init__.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/presidio-analyzer/presidio_analyzer/predefined_recognizers/__init__.py b/presidio-analyzer/presidio_analyzer/predefined_recognizers/__init__.py index 55dd6be840..3715da7c30 100644 --- a/presidio-analyzer/presidio_analyzer/predefined_recognizers/__init__.py +++ b/presidio-analyzer/presidio_analyzer/predefined_recognizers/__init__.py @@ -77,13 +77,11 @@ # Philippines recognizers from .country_specific.philippines.ph_tin_recognizer import PhTinRecognizer +from .country_specific.philippines.ph_umid_recognizer import PhUmidRecognizer # Poland recognizers from .country_specific.poland.pl_pesel_recognizer import PlPeselRecognizer -# Philippines recognizers -from .country_specific.philippines.ph_umid_recognizer import PhUmidRecognizer - # Singapore recognizers from .country_specific.singapore.sg_fin_recognizer import SgFinRecognizer from .country_specific.singapore.sg_uen_recognizer import SgUenRecognizer From 79ee42208a408e4d261e083d089f7dcefd09cfe2 Mon Sep 17 00:00:00 2001 From: Surya P R Date: Wed, 1 Jul 2026 20:27:37 +0530 Subject: [PATCH 7/7] chore: remove CHANGELOG.md modifications per reviewer request --- CHANGELOG.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dfde5ce3a1..e9ebd1bb44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,14 +63,6 @@ All notable changes to this project will be documented in this file. ### Anonymizer #### Added -- ISO 7064 Mod 11,10 structural checksum in `DeVatIdRecognizer`. Algorithm identical to `DeTaxIdRecognizer`; widely used by community validators (python-stdnum, VIES-adjacent). -- ICAO Doc 9303 MRZ checksum validation in `DePassportRecognizer` and `DeIdCardRecognizer` (weights 7, 3, 1 repeating; letters A=10…Z=35; sum mod 10). -- Structural validation improvements in `DeBsnrRecognizer` per KBV Arztnummern-Richtlinie Anlage 1; valid KV regional codes are defined for defense-in-depth/documentation purposes, but unknown prefixes are not currently rejected (no public checksum exists for BSNR). -- Turkish PII recognizer for `TR_NATIONAL_ID` (TCKN) to identify Turkish National Identification Numbers using pattern match, context, and NVI checksum validation. Disabled by default. -- Turkish phone number detection via configurable `PhoneRecognizer` with `supported_regions=["TR"]` and `supported_entity="TR_PHONE_NUMBER"`. Supports international (+90), national (0), and local formats using the `phonenumbers` library. Disabled by default; users enable it programmatically. -- Turkish PII recognizer for `TR_LICENSE_PLATE` (plaka) to identify Turkish vehicle license plates using pattern match, context, and province code validation (01-81). Disabled by default. -- Added PH_MOBILE_NUMBER recognizer for Philippine mobile phone numbers using PhoneRecognizer with supported_regions=['PH'] (disabled by default). -- Added PH_UMID recognizer for Philippine Unified Multi-Purpose ID (UMID) / Common Reference Number (CRN) using pattern match and context. Disabled by default. - Added `merge_entities_with_whitespace` support to `anonymize()` so adjacent analysis results can be merged across whitespace before anonymization (#1932) (Thanks @harishkernel) #### Changed