second commit

This commit is contained in:
2025-09-11 10:47:21 +03:00
parent 32954fcd59
commit b62640e39b
5 changed files with 6448 additions and 14 deletions

150
aigenerated.py Normal file
View File

@@ -0,0 +1,150 @@
import re
# --- Ресурсы для алгоритма ---
STOP_WORDS = {'пр.', 'лек.', 'лаб.', 'семинар'}
TITLES = ('доц.', 'проф.', 'асс.', 'ст.пр.')
SURNAME_ENDINGS = ('ов', 'ев', 'ин', 'ский', 'цкой', 'их', 'ых', 'ова', 'ева', 'ина', 'ская', 'ян', 'ко', "ня", "ин")
def is_surname_string(s: str) -> bool:
"""
Классифицирует строку, определяя, содержит ли она фамилию.
"""
if not isinstance(s, str) or not s:
return False
# Шаг 1: Очистка
s_clean = s.strip()
# Шаг 2: Жесткие правила "НЕ ФАМИЛИЯ"
if s_clean.lower() in STOP_WORDS:
return False
if s_clean.isupper() and len(s_clean) > 3:
return False
if re.search(r'[\(\)/«»%]', s_clean):
return False
words = s_clean.split()
if len(words) > 3:
return False
# Шаг 3: Жесткие правила "ТОЧНО ФАМИЛИЯ"
if re.search(r'\b[А-Я]\.?', s_clean): # Ищет "И.А." или "И.А"
return True
if s_clean.lower().startswith(TITLES):
return True
# Шаг 4: Эвристический анализ для оставшихся случаев
score = 0
# Правило на капитализацию
if words and words[0][0].isupper():
score += 5
else:
# Если слово не с большой буквы, это почти точно не фамилия
return False
# Правило на окончания
last_word = words[-1].lower()
if last_word.endswith(SURNAME_ENDINGS):
score += 6
# Правило на количество слов
if len(words) in [1, 2]:
score += 2
# Пороговое значение
THRESHOLD = 8
return score >= THRESHOLD
def extract_last_name(name_str: str) -> str or None:
"""
Извлекает из строки только фамилию.
Справляется с приклеенными званиями (типа "ст.пр.Дмитриев") и отбрасывает инициалы.
Ищет первое слово, написанное с заглавной буквы.
Args:
name_str: Исходная "грязная" строка с именем.
Returns:
Чистая фамилия в виде строки, или None, если фамилия не найдена.
"""
# Проверка, что на вход подана строка
if not isinstance(name_str, str):
return None
# Паттерн для поиска:
# [А-ЯЁ] - одна заглавная русская буква в начале
# [а-яё]+ - одна или более строчных русских букв после неё
# (?:-[А-ЯЁ][а-яё]+)? - опциональная часть для двойных фамилий (например, -Петров)
pattern = r'[А-ЯЁ][а-яё]+(?:-[А-ЯЁ][а-яё]+)?'
match = re.search(pattern, name_str)
if match:
return match.group(0) # Возвращаем найденное совпадение
else:
return None # Если ничего не найдено
# --- Шаг 0: Константы ---
POSITIVE_KEYWORDS = ['зал', 'ауд', 'каб', 'корп', 'кор.']
NEGATIVE_KEYWORDS = ['доц', 'проф', 'асс', 'лек', 'пр']
# Транслитерация для унификации
TRANS_TABLE = str.maketrans('TCBAHIMK', 'ТКВАНІМК') # Латиница -> Кириллица
def is_room_number(s: str) -> bool:
"""
Проверяет, является ли строка номером кабинета, по многоуровневому алгоритму.
"""
# --- Шаг 1: Быстрые негативные фильтры ---
if not isinstance(s, str) or not s.strip():
return False # 1. Проверка на пустоту
if ',' in s:
return False # 2. Проверка на запятые (даты)
# 3. Проверка на "очевидный мусор"
first_word = s.strip().lower().split()[0]
if first_word in NEGATIVE_KEYWORDS:
return False
# 4. Проверка на инициалы (напр. А.Е.)
if re.search(r'\b[А-Я]\.[А-Я]\.?\b', s):
return False
# 5. Проверка на длинное слово без цифр
if not any(char.isdigit() for char in s) and len(s.split()) == 1 and len(s) > 10:
return False
# --- Шаг 2: Быстрые позитивные фильтры ---
s_lower = s.lower()
if any(keyword in s_lower for keyword in POSITIVE_KEYWORDS):
return True # 1. Проверка по ключевым словам
# --- Шаг 3: Основной анализ ---
if not any(char.isdigit() for char in s):
return False # 1. Требуется наличие цифр
# 2. Создание "чистой" версии
clean_s = s.upper().translate(TRANS_TABLE).replace(' ', '')
# 3. Комплексный паттерн для проверки
# Пояснение:
# ^...$ - шаблон должен соответствовать всей строке
# [А-ЯЁ]?-? - необязательная буква и необязательный дефис в начале
# \d+ - одна или более цифр (ядро номера)
# (?:[.-]\d+)* - необязательные группы ".число" или "-число"
# [А-ЯЁ]?$ - необязательная буква в конце
room_pattern = re.compile(r'^[А-ЯЁ]?-?\d+(?:[.-]\d+)*[А-ЯЁ]?$')
if room_pattern.fullmatch(clean_s):
return True
# --- Шаг 4: Финальное решение ---
return False