refactor: подтянуть Legal NER, переформатировать код и добавить тесты
Слияние с 685ec97 (третья ступень NER для юридических реквизитов),
код приведён к google-java-format, добавлены юнит-тесты
AdaptiveConcurrencyLimiter/SystemsConfig/PayloadCipher.
This commit is contained in:
@@ -1,7 +1,5 @@
|
||||
package ru.pdguard.detect;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static ru.pdguard.detect.RulePatterns.CITIZENSHIP_GAP;
|
||||
import static ru.pdguard.detect.RulePatterns.CITIZENSHIP_VALUE;
|
||||
import static ru.pdguard.detect.RulePatterns.ORGANISATION_NEARBY;
|
||||
@@ -9,124 +7,165 @@ import static ru.pdguard.detect.RulePatterns.ROLE_GAP;
|
||||
import static ru.pdguard.detect.RulePatterns.STREET_NAME;
|
||||
import static ru.pdguard.detect.RuleRegistry.ADDRESS_NEARBY;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/** Правила распознавания органа выдачи паспорта, места рождения, гражданства и адреса. */
|
||||
final class AddressRules {
|
||||
|
||||
private AddressRules() {
|
||||
}
|
||||
private AddressRules() {}
|
||||
|
||||
static final List<Rule> RULES = List.of(
|
||||
static final List<Rule> RULES =
|
||||
List.of(
|
||||
|
||||
// «выдан ОУФМС России по г. Москве 12.05.2015» — дата в состав органа не входит,
|
||||
// её забирает отдельное правило. Приоритет выше городского, иначе от органа
|
||||
// осталась бы замаскированной только его часть.
|
||||
// Перечень форм, не голая основа «выда»: она зацепила бы и «выдающийся»
|
||||
// (обычное слово, не про выдачу документа).
|
||||
Rule.of(PdTypes.PASSPORT_ISSUER, "(?iu:выдан|выдал[аио]?|выдали|выдач[аи]|выдаче)"
|
||||
+ "\\W{0,3}([^,;\\n]{3,90}?)"
|
||||
+ "(?=\\s*\\d{1,2}[.\\-/]\\d{1,2}[.\\-/]\\d{2,4}|[,;\\n]|\\s*$)", 78)
|
||||
.groups(1)
|
||||
.anchoredBy("выдан", "выдал", "выдач"),
|
||||
// «выдан ОУФМС России по г. Москве 12.05.2015» — дата в состав органа не входит,
|
||||
// её забирает отдельное правило. Приоритет выше городского, иначе от органа
|
||||
// осталась бы замаскированной только его часть.
|
||||
// Перечень форм, не голая основа «выда»: она зацепила бы и «выдающийся»
|
||||
// (обычное слово, не про выдачу документа).
|
||||
Rule.of(
|
||||
PdTypes.PASSPORT_ISSUER,
|
||||
"(?iu:выдан|выдал[аио]?|выдали|выдач[аи]|выдаче)"
|
||||
+ "\\W{0,3}([^,;\\n]{3,90}?)"
|
||||
+ "(?=\\s*\\d{1,2}[.\\-/]\\d{1,2}[.\\-/]\\d{2,4}|[,;\\n]|\\s*$)",
|
||||
78)
|
||||
.groups(1)
|
||||
.anchoredBy("выдан", "выдал", "выдач"),
|
||||
|
||||
// «совпадает с указанным в анкете: X» — второе упоминание органа выдачи
|
||||
// под собственным якорем, без бэкреференса на первое.
|
||||
Rule.of(PdTypes.PASSPORT_ISSUER, "(?iu:указанн\\w*\\s+в\\s+анкете)\\W{0,5}([^,;.\\n]{3,90}?)"
|
||||
+ "(?=[,;.\\n]|\\s*$)", 78)
|
||||
.groups(1)
|
||||
.anchoredBy("указанн"),
|
||||
// «совпадает с указанным в анкете: X» — второе упоминание органа выдачи
|
||||
// под собственным якорем, без бэкреференса на первое.
|
||||
Rule.of(
|
||||
PdTypes.PASSPORT_ISSUER,
|
||||
"(?iu:указанн\\w*\\s+в\\s+анкете)\\W{0,5}([^,;.\\n]{3,90}?)"
|
||||
+ "(?=[,;.\\n]|\\s*$)",
|
||||
78)
|
||||
.groups(1)
|
||||
.anchoredBy("указанн"),
|
||||
|
||||
// «Орган выдачи УФМС России по Республике Татарстан» — орган после якоря,
|
||||
// до слова «совпадает» или конца фразы.
|
||||
Rule.of(PdTypes.PASSPORT_ISSUER, "(?iu:орган\\s+выдачи)\\W{0,5}([^,;:\\n]{3,90}?)"
|
||||
+ "(?=\\s*(?iu:совпадает|указанн)|[,;:\\n]|\\s*$)", 78)
|
||||
.groups(1)
|
||||
.anchoredBy("орган выдачи"),
|
||||
// «Орган выдачи УФМС России по Республике Татарстан» — орган после якоря,
|
||||
// до слова «совпадает» или конца фразы.
|
||||
Rule.of(
|
||||
PdTypes.PASSPORT_ISSUER,
|
||||
"(?iu:орган\\s+выдачи)\\W{0,5}([^,;:\\n]{3,90}?)"
|
||||
+ "(?=\\s*(?iu:совпадает|указанн)|[,;:\\n]|\\s*$)",
|
||||
78)
|
||||
.groups(1)
|
||||
.anchoredBy("орган выдачи"),
|
||||
Rule.of(
|
||||
PdTypes.BIRTH_PLACE,
|
||||
"(?iu:мест\\w*\\s+рождения)\\W{0,5}([^,;\\n]{3,60}?)(?=\\s*[,;\\n]|\\s*$)",
|
||||
76)
|
||||
.groups(1)
|
||||
.anchoredBy("рождения"),
|
||||
Rule.of(
|
||||
PdTypes.BIRTH_PLACE,
|
||||
"(?iu:родил(?:ся|ась))[^,;\\n]{0,40}?\\s+в\\s+"
|
||||
+ "([^,;\\n]{3,40}?)(?=\\s*[,;\\n]|\\s*$)",
|
||||
76)
|
||||
.groups(1)
|
||||
.anchoredBy("родил"),
|
||||
|
||||
Rule.of(PdTypes.BIRTH_PLACE, "(?iu:мест\\w*\\s+рождения)\\W{0,5}([^,;\\n]{3,60}?)(?=\\s*[,;\\n]|\\s*$)", 76)
|
||||
.groups(1)
|
||||
.anchoredBy("рождения"),
|
||||
// ROLE_GAP, не \W{0,5}: «Гражданство бенефициара по договору страхования: Х» —
|
||||
// между якорем и значением бывает несколько слов, не только пунктуация.
|
||||
// Список через запятую/слэш — вторая опциональная группа тем же шаблоном.
|
||||
Rule.of(
|
||||
PdTypes.CITIZENSHIP,
|
||||
"(?iu:гражданств)\\w*"
|
||||
+ CITIZENSHIP_GAP
|
||||
+ "("
|
||||
+ CITIZENSHIP_VALUE
|
||||
+ ")(?:\\s*[,/]\\s*("
|
||||
+ CITIZENSHIP_VALUE
|
||||
+ "))?",
|
||||
80)
|
||||
.groups(1, 2)
|
||||
.validatedBy(CountryDictionary::isKnownCountry)
|
||||
.anchoredBy("гражданств"),
|
||||
|
||||
Rule.of(PdTypes.BIRTH_PLACE, "(?iu:родил(?:ся|ась))[^,;\\n]{0,40}?\\s+в\\s+"
|
||||
+ "([^,;\\n]{3,40}?)(?=\\s*[,;\\n]|\\s*$)", 76)
|
||||
.groups(1)
|
||||
.anchoredBy("родил"),
|
||||
// ин/ка/ина/ки — именительный/родительный; ином/кой — творительный
|
||||
// («гражданином», «гражданкой»).
|
||||
Rule.of(
|
||||
PdTypes.CITIZENSHIP,
|
||||
"(?iu:граждан(?:ин|ка|ина|ки|ином|кой))\\b\\s+"
|
||||
+ "("
|
||||
+ CITIZENSHIP_VALUE
|
||||
+ ")(?:\\s*[,/]\\s*("
|
||||
+ CITIZENSHIP_VALUE
|
||||
+ "))?",
|
||||
75)
|
||||
.groups(1, 2)
|
||||
.validatedBy(CountryDictionary::isKnownCountry)
|
||||
.anchoredBy("граждан"),
|
||||
|
||||
// ROLE_GAP, не \W{0,5}: «Гражданство бенефициара по договору страхования: Х» —
|
||||
// между якорем и значением бывает несколько слов, не только пунктуация.
|
||||
// Список через запятую/слэш — вторая опциональная группа тем же шаблоном.
|
||||
Rule.of(PdTypes.CITIZENSHIP, "(?iu:гражданств)\\w*" + CITIZENSHIP_GAP
|
||||
+ "(" + CITIZENSHIP_VALUE + ")(?:\\s*[,/]\\s*(" + CITIZENSHIP_VALUE + "))?", 80)
|
||||
.groups(1, 2)
|
||||
.validatedBy(CountryDictionary::isKnownCountry)
|
||||
.anchoredBy("гражданств"),
|
||||
// --- Адрес: каждая составляющая настраивается отдельно ---
|
||||
|
||||
// ин/ка/ина/ки — именительный/родительный; ином/кой — творительный
|
||||
// («гражданином», «гражданкой»).
|
||||
Rule.of(PdTypes.CITIZENSHIP, "(?iu:граждан(?:ин|ка|ина|ки|ином|кой))\\b\\s+"
|
||||
+ "(" + CITIZENSHIP_VALUE + ")(?:\\s*[,/]\\s*(" + CITIZENSHIP_VALUE + "))?", 75)
|
||||
.groups(1, 2)
|
||||
.validatedBy(CountryDictionary::isKnownCountry)
|
||||
.anchoredBy("граждан"),
|
||||
Rule.of(PdTypes.ADDRESS_POSTCODE, "(?iu:индекс)\\w*" + ROLE_GAP + "(\\d{6})\\b", 74)
|
||||
.groups(1)
|
||||
.vetoedBy(ORGANISATION_NEARBY)
|
||||
.anchoredBy("индекс"),
|
||||
Rule.of(
|
||||
PdTypes.ADDRESS_POSTCODE,
|
||||
"\\b(\\d{6})(?=\\s*,?\\s*(?iu:г\\.|город|обл\\.|область|респ|край))",
|
||||
74)
|
||||
.groups(1)
|
||||
.vetoedBy(ORGANISATION_NEARBY),
|
||||
|
||||
// --- Адрес: каждая составляющая настраивается отдельно ---
|
||||
// Не только «г.»: перепись, на которой проверяется словарь, покрывает
|
||||
// сёла, посёлки, деревни, хутора и станицы — «рп. Ильинское», «с. Кукуево»
|
||||
// из ТЗ без этих якорей не нашлись бы вообще, город там ни при чём.
|
||||
Rule.of(
|
||||
PdTypes.ADDRESS_CITY,
|
||||
"(?iu:\\bг\\.|\\bгор\\.|\\bгород|\\bрп\\.|\\bпгт\\.?|\\bп\\.|\\bс\\.|\\bсело\\b"
|
||||
+ "|\\bд\\.|\\bдеревня\\b|\\bдер\\.|\\bх\\.|\\bхутор\\b|\\bст-ца|\\bстаница|\\bаул\\b"
|
||||
+ "|\\bсл\\.|\\bслобода\\b|\\bаал\\b)\\s?(\\p{Lu}[\\p{L}-]{1,30})\\b",
|
||||
73)
|
||||
.groups(1)
|
||||
.validatedBy(ToponymDictionary::isKnownSettlement)
|
||||
.vetoedBy(ORGANISATION_NEARBY)
|
||||
.anchoredBy(
|
||||
"г.", "гор", "город", "рп.", "пгт", "п.", "с.", "село", "д.", "деревня", "дер.",
|
||||
"х.", "хутор", "ст-ца", "станица", "аул", "сл.", "слобода", "аал"),
|
||||
Rule.of(
|
||||
PdTypes.ADDRESS_STREET,
|
||||
"(?iu:\\bул\\.|\\bулиц\\p{L}*|\\bпр-т|\\bпроспект\\p{L}*|\\bпер\\.|\\bпереул\\p{L}*"
|
||||
+ "|\\bш\\.|\\bшоссе|\\bб-р|\\bбульвар\\p{L}*|\\bнаб\\.|\\bнабережн\\p{L}*)"
|
||||
+ "\\W{0,3}("
|
||||
+ STREET_NAME
|
||||
+ ")",
|
||||
73)
|
||||
.groups(1)
|
||||
.vetoedBy(ORGANISATION_NEARBY)
|
||||
.requiringNear(ADDRESS_NEARBY)
|
||||
.anchoredBy("ул", "просп", "пр-т", "пер.", "шоссе", "ш.", "бульвар", "б-р", "наб"),
|
||||
|
||||
Rule.of(PdTypes.ADDRESS_POSTCODE, "(?iu:индекс)\\w*" + ROLE_GAP + "(\\d{6})\\b", 74)
|
||||
.groups(1)
|
||||
.vetoedBy(ORGANISATION_NEARBY)
|
||||
.anchoredBy("индекс"),
|
||||
|
||||
Rule.of(PdTypes.ADDRESS_POSTCODE,
|
||||
"\\b(\\d{6})(?=\\s*,?\\s*(?iu:г\\.|город|обл\\.|область|респ|край))", 74)
|
||||
.groups(1)
|
||||
.vetoedBy(ORGANISATION_NEARBY),
|
||||
|
||||
// Не только «г.»: перепись, на которой проверяется словарь, покрывает
|
||||
// сёла, посёлки, деревни, хутора и станицы — «рп. Ильинское», «с. Кукуево»
|
||||
// из ТЗ без этих якорей не нашлись бы вообще, город там ни при чём.
|
||||
Rule.of(PdTypes.ADDRESS_CITY, "(?iu:\\bг\\.|\\bгор\\.|\\bгород|\\bрп\\.|\\bпгт\\.?|\\bп\\.|\\bс\\.|\\bсело\\b"
|
||||
+ "|\\bд\\.|\\bдеревня\\b|\\bдер\\.|\\bх\\.|\\bхутор\\b|\\bст-ца|\\bстаница|\\bаул\\b"
|
||||
+ "|\\bсл\\.|\\bслобода\\b|\\bаал\\b)\\s?(\\p{Lu}[\\p{L}-]{1,30})\\b", 73)
|
||||
.groups(1)
|
||||
.validatedBy(ToponymDictionary::isKnownSettlement)
|
||||
.vetoedBy(ORGANISATION_NEARBY)
|
||||
.anchoredBy("г.", "гор", "город", "рп.", "пгт", "п.", "с.", "село", "д.", "деревня",
|
||||
"дер.", "х.", "хутор", "ст-ца", "станица", "аул", "сл.", "слобода", "аал"),
|
||||
|
||||
Rule.of(PdTypes.ADDRESS_STREET,
|
||||
"(?iu:\\bул\\.|\\bулиц\\p{L}*|\\bпр-т|\\bпроспект\\p{L}*|\\bпер\\.|\\bпереул\\p{L}*"
|
||||
+ "|\\bш\\.|\\bшоссе|\\bб-р|\\bбульвар\\p{L}*|\\bнаб\\.|\\bнабережн\\p{L}*)"
|
||||
+ "\\W{0,3}(" + STREET_NAME + ")", 73)
|
||||
.groups(1)
|
||||
.vetoedBy(ORGANISATION_NEARBY)
|
||||
.requiringNear(ADDRESS_NEARBY)
|
||||
.anchoredBy("ул", "просп", "пр-т", "пер.", "шоссе", "ш.", "бульвар", "б-р", "наб"),
|
||||
|
||||
// «Невский пр-т» — указатель после названия. Форма слишком общая, поэтому
|
||||
// принимается только рядом с другими частями адреса: иначе под маску попал бы
|
||||
// любой рассказ про Невский проспект.
|
||||
Rule.of(PdTypes.ADDRESS_STREET, "\\b(\\p{Lu}[\\p{L}-]{2,30})\\s+"
|
||||
+ "(?iu:пр-т|проспект|улиц\\p{L}*|шоссе|бульвар|переул\\p{L}*|набережн\\p{L}*)\\b", 73)
|
||||
.groups(1)
|
||||
.vetoedBy(ORGANISATION_NEARBY)
|
||||
.requiringNear(ADDRESS_NEARBY)
|
||||
.anchoredBy("пр-т", "проспект", "улиц", "шоссе", "бульвар", "переул", "набережн"),
|
||||
|
||||
Rule.of(PdTypes.ADDRESS_HOUSE,
|
||||
"(?iu:\\bд\\.|\\bдом)\\s?(\\d+\\p{L}?(?:\\s?(?iu:к\\.|корп\\.?|стр\\.)\\s?\\d+)?)\\b", 72)
|
||||
.groups(1)
|
||||
.vetoedBy(ORGANISATION_NEARBY)
|
||||
.anchoredBy("д.", "дом"),
|
||||
|
||||
Rule.of(PdTypes.ADDRESS_FLAT, "(?iu:\\bкв\\.|\\bквартир\\p{L}*)\\s?(\\d+\\p{L}?)\\b", 72)
|
||||
.groups(1)
|
||||
.vetoedBy(ORGANISATION_NEARBY)
|
||||
.anchoredBy("кв"),
|
||||
|
||||
Rule.of(PdTypes.ADDRESS_COUNTRY,
|
||||
"(?iu:стран\\p{L}*(?:\\s+(?:регистрации|проживания|гражданства))?)"
|
||||
+ "\\W{0,5}(\\p{Lu}[\\p{L}-]{2,30})\\b", 71)
|
||||
.groups(1)
|
||||
.vetoedBy(ORGANISATION_NEARBY)
|
||||
.anchoredBy("стран")
|
||||
);
|
||||
// «Невский пр-т» — указатель после названия. Форма слишком общая, поэтому
|
||||
// принимается только рядом с другими частями адреса: иначе под маску попал бы
|
||||
// любой рассказ про Невский проспект.
|
||||
Rule.of(
|
||||
PdTypes.ADDRESS_STREET,
|
||||
"\\b(\\p{Lu}[\\p{L}-]{2,30})\\s+"
|
||||
+ "(?iu:пр-т|проспект|улиц\\p{L}*|шоссе|бульвар|переул\\p{L}*|набережн\\p{L}*)\\b",
|
||||
73)
|
||||
.groups(1)
|
||||
.vetoedBy(ORGANISATION_NEARBY)
|
||||
.requiringNear(ADDRESS_NEARBY)
|
||||
.anchoredBy("пр-т", "проспект", "улиц", "шоссе", "бульвар", "переул", "набережн"),
|
||||
Rule.of(
|
||||
PdTypes.ADDRESS_HOUSE,
|
||||
"(?iu:\\bд\\.|\\bдом)\\s?(\\d+\\p{L}?(?:\\s?(?iu:к\\.|корп\\.?|стр\\.)\\s?\\d+)?)\\b",
|
||||
72)
|
||||
.groups(1)
|
||||
.vetoedBy(ORGANISATION_NEARBY)
|
||||
.anchoredBy("д.", "дом"),
|
||||
Rule.of(PdTypes.ADDRESS_FLAT, "(?iu:\\bкв\\.|\\bквартир\\p{L}*)\\s?(\\d+\\p{L}?)\\b", 72)
|
||||
.groups(1)
|
||||
.vetoedBy(ORGANISATION_NEARBY)
|
||||
.anchoredBy("кв"),
|
||||
Rule.of(
|
||||
PdTypes.ADDRESS_COUNTRY,
|
||||
"(?iu:стран\\p{L}*(?:\\s+(?:регистрации|проживания|гражданства))?)"
|
||||
+ "\\W{0,5}(\\p{Lu}[\\p{L}-]{2,30})\\b",
|
||||
71)
|
||||
.groups(1)
|
||||
.vetoedBy(ORGANISATION_NEARBY)
|
||||
.anchoredBy("стран"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user