Skip to content
6 changes: 6 additions & 0 deletions docs/supported_entities.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,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 |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,13 @@ recognizers:
enabled: false
country_code: tr

- name: PhUmidRecognizer
supported_languages:
- en
type: predefined
enabled: false
country_code: ph

- name: HuggingFaceNerRecognizer
supported_languages:
- en
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@

# 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
Expand Down Expand Up @@ -223,6 +224,7 @@
"HuggingFaceNerRecognizer",
"PhTinRecognizer",
"PlPeselRecognizer",
"PhUmidRecognizer",
"AzureAILanguageRecognizer",
"InAadhaarRecognizer",
"InGstinRecognizer",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
"""Philippines-specific recognizers package."""

from .ph_tin_recognizer import PhTinRecognizer
from .ph_umid_recognizer import PhUmidRecognizer

__all__ = ["PhTinRecognizer"]
__all__ = [
"PhTinRecognizer",
"PhUmidRecognizer",
]
Original file line number Diff line number Diff line change
@@ -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,
)
98 changes: 98 additions & 0 deletions presidio-analyzer/tests/test_ph_umid_recognizer.py
Original file line number Diff line number Diff line change
@@ -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.2, 1.0),)),
("CRN: 0111-1234567-8", 1, ((5, 19),), ((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),)),
("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", 1, ((0, 12),), ((0.2, 1.0),)),
("987654321098", 1, ((0, 12),), ((0.2, 1.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"
Loading