Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [2.6.9] - 2026-07

### Fixed
- Official letter openings are no longer masked as PIB:
`Повідомляємо Вам, що …` was treated as "Surname Name". Words with
1st/2nd-person-plural verb endings (`-ємо`, `-имо`, `-емо`, `-єте`,
`-ите`, `-ете`) are rejected as name candidates; pronouns
`Вам/Вами/Ваш(-а/-е)` and common verbs (`Повідомляємо`, `Просимо`,
`Направляємо`, `Надаємо`) added to `EXCLUDE_WORDS`. Real PIB after such
phrases is still masked.

## [2.6.8] - 2026-07

### Added
Expand Down
2 changes: 1 addition & 1 deletion data_masking.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
# Re-exports from masking package for backward compatibility
# ============================================================================

__version__ = "2.6.8"
__version__ = "2.6.9"

from masking.constants import (
__version__, __author__, __contact__, __phone__, __license__, __year__,
Expand Down
5 changes: 4 additions & 1 deletion masking/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
# ============================================================================
# МЕТАДАНІ
# ============================================================================
__version__ = "2.6.8"
__version__ = "2.6.9"
__author__ = "Vladyslav V. Prodan"
__contact__ = "github.com/click0"
__phone__ = "+38(099)6053340"
Expand Down Expand Up @@ -118,6 +118,9 @@
"доповідаю", "прошу", "наказую", "призначити", "звільнити",
"проявив", "виконав", "оголосити", "оголосити",
"про", "по", "від", "до", "за", "суті", "мною", "Вас", "вас",
"Вам", "вам", "Вами", "вами", "Ваш", "ваш", "Ваша", "ваша", "Ваше", "ваше",
"Повідомляємо", "повідомляємо", "Просимо", "просимо",
"Направляємо", "направляємо", "Надаємо", "надаємо",
"зв'язку", "порушення", "вимог",
"Збройних", "збройних", "України", "україни", "служби", "Служби",
"військової", "Військової", "частини", "Частини", "взводу", "Взводу",
Expand Down
4 changes: 4 additions & 0 deletions masking/language.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ def looks_like_name(word: str) -> bool:
clean_word = word.strip(_cfg.QUOTE_CHARS).rstrip(',.!?;:')
if len(clean_word) < 3: return False
if '.' in clean_word: return False
# Дієслова 1-2 особи множини (Повідомляємо, Просимо, Надаєте) —
# ніколи не імена/прізвища
if clean_word.lower().endswith(('ємо', 'имо', 'емо', 'єте', 'ите', 'ете')):
return False
if clean_word.lower() in _cfg.EXCLUDE_WORDS_LOWER: return False
if re.search(r'\d', clean_word): return False
if clean_word.lower() in _cfg.RANKS_LIST_LOWER: return False
Expand Down
25 changes: 25 additions & 0 deletions tests/test_quoted.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,28 @@ def test_two_distinct_quoted_ranks_roundtrip(self):
def test_non_rank_in_quotes_untouched(self):
assert mask('слово «важливо» тут')[0] == 'слово «важливо» тут'
assert mask('позиція «138» ключ')[0] == 'позиція «138» ключ'


class TestOfficialPhrases:
"""Канцелярські звороти не маскуються як ПІБ (v2.6.9)."""

def test_povidomlyaemo_vam_untouched(self):
t = ('Повідомляємо Вам, що відповідно до пункту 15 розділу XII '
'Інструкції з організації обліку особового складу в системі '
'Міністерства оборони України,')
masked, _ = mask(t)
assert masked == t, f"Канцеляризм замасковано: {masked!r}"

def test_first_person_plural_verbs_not_names(self):
for t in [
'Просимо Вас надати необхідні документи до кінця тижня',
'Направляємо Вам копію наказу для ознайомлення згідно вимог',
]:
masked, _ = mask(t)
assert masked == t, f"Хибне спрацювання: {t!r} -> {masked!r}"

def test_real_pib_after_official_phrase_still_masked(self):
t = 'Повідомляємо, що сержант Коваленко Марія Іванівна прибула'
masked, _ = mask(t)
assert 'Коваленко' not in masked
assert masked.startswith('Повідомляємо, що ')
2 changes: 1 addition & 1 deletion unmasking/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
# ============================================================================
# МЕТАДАНІ
# ============================================================================
__version__ = "2.6.8"
__version__ = "2.6.9"


def main():
Expand Down
Loading