fruitpicker01 commited on
Commit
3bd810e
·
verified ·
1 Parent(s): 3656ed8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +382 -224
app.py CHANGED
@@ -909,35 +909,56 @@ def reset_button_text_2():
909
 
910
  def check_source_fields(description, product_name, benefits, key_message):
911
  results = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
912
 
913
  # Проверяем "Описание предложения"
914
  desc_checks = perform_checks(description, "")
915
- not_passed_desc = extract_failed_checks(desc_checks)
916
  if not_passed_desc:
917
  results.append(f"Описание предложения:\n{not_passed_desc}")
918
 
919
  # Проверяем "Наименование продукта"
920
  name_checks = perform_checks(product_name, "")
921
- not_passed_name = extract_failed_checks(name_checks)
922
  if not_passed_name:
923
  results.append(f"Наименование продукта:\n{not_passed_name}")
924
 
925
  # Проверяем "Преимущества"
926
  ben_checks = perform_checks(benefits, "")
927
- not_passed_ben = extract_failed_checks(ben_checks)
928
  if not_passed_ben:
929
  results.append(f"Преимущества:\n{not_passed_ben}")
930
 
931
  # Проверяем "Ключевое сообщение"
932
  km_checks = perform_checks(key_message, "")
933
- not_passed_km = extract_failed_checks(km_checks)
934
  if not_passed_km:
935
  results.append(f"Ключевое сообщение:\n{not_passed_km}")
936
 
937
  if not results:
938
- return "Проверка исходных данных пройдена"
939
  else:
940
- return "\n\n".join(results)
 
941
 
942
 
943
  def on_check_source_fields(description, product_name, benefits, key_message):
@@ -948,7 +969,16 @@ def on_check_source_fields(description, product_name, benefits, key_message):
948
 
949
 
950
  def extract_failed_checks(checks_dict):
 
 
 
951
  lines = []
 
 
 
 
 
 
952
  for rule_key, result in checks_dict.items():
953
  # Определяем, было ли нарушение
954
  if isinstance(result, tuple):
@@ -989,12 +1019,29 @@ def rule_to_str(rule_key):
989
 
990
  # ФУНКЦИИ ПРОВЕРОК (НАЧАЛО)
991
 
 
 
 
 
 
 
 
 
 
 
992
  # 1. Запрещенные слова
993
 
994
- def check_forbidden_words(message):
995
- morph = pymorphy3.MorphAnalyzer()
 
 
 
 
 
 
 
996
 
997
- # Перечень запрещённых слов и фраз
998
  forbidden_patterns = [
999
  r'№\s?1\b', r'номер\sодин\b', r'номер\s1\b',
1000
  r'вкусный', r'дешёвый', r'продукт',
@@ -1003,169 +1050,233 @@ def check_forbidden_words(message):
1003
  r'гарантия', r'успех', r'лидер', 'никакой'
1004
  ]
1005
 
1006
- # Удаляем знаки препинания для корректного анализа
1007
- message_without_punctuation = message.translate(str.maketrans('', '', string.punctuation))
1008
 
1009
- # Замена всех слов, содержащих "бессроч", на временное значение
1010
  placeholder = "заменабессроч"
1011
- message_without_punctuation = re.sub(r'\b\w*бессроч\w*\b', placeholder, message_without_punctuation,
1012
- flags=re.IGNORECASE)
1013
-
1014
- # Проверка на наличие подстроки "лучш" (без учета регистра)
1015
- if re.search(r'лучш', message_without_punctuation, re.IGNORECASE):
1016
- return (False, 'Есть слово "лучший"')
1017
-
1018
- # Лемматизация слов сообщения
1019
- words = message_without_punctuation.split()
1020
- lemmas = [morph.parse(word)[0].normal_form for word in words]
1021
 
1022
- # Восстановление всех слов с подстрокой "бессроч"
1023
- lemmas = [re.sub(r'заменабессроч', 'бессроч', word) for word in lemmas]
1024
- normalized_message = ' '.join(lemmas)
 
 
1025
 
1026
- # Проверка на запрещённые фразы и леммы
1027
  for pattern in forbidden_patterns:
1028
- if re.search(pattern, normalized_message, re.IGNORECASE):
1029
- print(f"Не пройдена проверка: Запрещенные слова. Сообщение: {message}")
1030
- return (False, f'Запрещенное слово: {pattern}')
 
 
 
 
 
1031
 
1032
  return True
1033
 
1034
 
1035
  # 2 и #3. Обращение к клиенту и приветствие клиента
1036
 
1037
- def check_no_greeting(message):
1038
- morph = pymorphy3.MorphAnalyzer()
1039
- # Список типичных обращений и приветствий
 
 
 
 
 
 
1040
  greeting_patterns = [
1041
  r"привет\b", r"здравствуй", r"добрый\s(день|вечер|утро)",
1042
  r"дорогой\b", r"уважаемый\b", r"дорогая\b", r"уважаемая\b",
1043
  r"господин\b", r"госпожа\b", r"друг\b", r"коллега\b",
1044
- r"товарищ\b", r"приятель\b", r"друг\b", r"подруга\b"
1045
  ]
1046
-
1047
- # Компилируем все шаблоны в один регулярное выражение
1048
- greeting_regex = re.compile('|'.join(greeting_patterns), re.IGNORECASE)
1049
-
1050
- # Проверяем, начинается ли сообщение с шаблона приветствия или обращения
1051
- if greeting_regex.search(message.strip()):
1052
- print(f"Не пройдена проверка: Обращение к клиенту и приветствие клиента. Сообщение: {message}")
1053
- return (False, 'Есть приветствие')
 
1054
  return True
1055
 
1056
 
1057
  # 4. Обещания и гарантии
1058
 
1059
- def check_no_promises(message):
 
 
 
 
 
 
 
 
1060
  morph = pymorphy3.MorphAnalyzer()
1061
- promise_patterns = [
1062
- "обещать", "обещание", "гарантировать", "обязаться", "обязать", "обязательство", "обязательный"
1063
- ]
1064
 
1065
  words = message.split()
1066
- lemmas = [morph.parse(word)[0].normal_form for word in words]
1067
 
1068
- for pattern in promise_patterns:
1069
- if pattern in lemmas:
1070
- print(f"Не пройдена проверка: Обещания и гарантии. Сообщение: {message}")
1071
- return False, f'Не пройдена проверка: Обещания и гарантии:{pattern}'
1072
  return True
1073
 
1074
 
1075
  # 5. Составные конструкции из двух глаголов
1076
 
1077
- def check_no_double_verbs(message):
 
 
 
 
 
 
 
 
1078
  morph = pymorphy3.MorphAnalyzer()
1079
- # Разделяем текст по пробелам и знакам препинания
1080
  words = re.split(r'\s+|[.!?]', message)
1081
- morphs = [morph.parse(word)[0] for word in words]
1082
-
1083
- for i in range(len(morphs) - 1):
1084
- # Проверяем, что оба слова являются глаголами (в любой форме, включая инфинитивы)
1085
- if (morphs[i].tag.POS in {'VERB', 'INFN'}) and (morphs[i + 1].tag.POS in {'VERB', 'INFN'}):
1086
- # Проверяем, является ли первый глагол "хотеть" или "начинать"
1087
- if morphs[i].normal_form in ['хотеть', 'начинать', 'начать']:
1088
- return True
1089
- else:
1090
- print(f"Не пройдена проверка: Составные конструкции из двух глаголов. Сообщение: {message}")
1091
- return False, f'Не пройдена проверка на составные конструкции из двух глаголов: {morphs[i].word} {morphs[i + 1].word}'
 
 
 
 
 
1092
  return True
1093
 
1094
 
1095
  # 6. Причастия и причастные обороты
1096
 
1097
- def check_no_participles(message):
 
 
 
 
 
 
 
 
 
 
1098
  morph = pymorphy3.MorphAnalyzer()
1099
  words = message.split()
1100
- exceptions = {"повысить", "увеличить", "понизить", "снизить"}
1101
-
1102
- for word in words:
1103
- parsed_word = morph.parse(word)[0]
1104
- lemma = parsed_word.normal_form
1105
- if 'PRTF' in parsed_word.tag and lemma not in exceptions:
1106
- print(f"Не пройдена проверка: Причастия и причастные обороты. Сообщение: {message}")
1107
- return False, f'Не пройдена проверка на причастия: {parsed_word.word}'
1108
  return True
1109
 
1110
 
1111
  # 7. Деепричастия и деепричастные обороты
1112
 
1113
- def check_no_adverbial_participles(message):
 
 
 
 
 
 
 
 
1114
  morph = pymorphy3.MorphAnalyzer()
1115
  words = message.split()
1116
- morphs = [morph.parse(word)[0] for word in words]
1117
-
1118
- for morph in morphs:
1119
- if 'GRND' in morph.tag:
1120
- print(f"Не пройдена проверка: Деепричастия и деепричастные обороты. Сообщение: {message}")
1121
- return False, f'Не пройдена проверка на деепричастия: {morph.word}'
1122
  return True
1123
 
1124
 
1125
  # 8. Превосходная степень прилагательных
1126
 
1127
- def check_no_superlative_adjectives(message):
1128
- morph = pymorphy3.MorphAnalyzer()
1129
- words = message.split()
1130
- morphs = [morph.parse(word)[0] for word in words]
 
 
 
 
1131
 
1132
- for morph in morphs:
1133
- if 'Supr' in morph.tag:
1134
- print(f"Не пройдена проверка: Превосходная степень прилагательных. Сообщение: {message}")
1135
- return False, f'Не пройдена проверка на превосходную степерь прилагательного: {morph.word}'
 
 
 
1136
  return True
1137
 
1138
 
1139
  # 9. Страдательный залог
1140
 
1141
- def check_no_passive_voice(message):
 
 
 
 
 
 
 
 
1142
  morph = pymorphy3.MorphAnalyzer()
1143
- # Разбиваем сообщение на слова, игнорируя пунктуацию
1144
  words = re.findall(r'\b\w+(?:-\w+)*\b', message.lower())
1145
 
1146
- for word in words:
1147
- parse = morph.parse(word)[0] # Берём только первый разбор
1148
- if 'pssv' in parse.tag:
1149
- return False, f'Не пройдена проверка на страдательный залог: {word}'
 
 
1150
  return True
1151
 
1152
 
1153
  # 10. Порядковые числительные от 10 прописью
1154
 
1155
- def check_no_written_out_ordinals(message):
 
 
 
 
 
 
 
 
1156
  morph = pymorphy3.MorphAnalyzer()
1157
  ordinal_words = [
1158
- "десятый", "одиннадцатый", "двенадцатый", "тринадцатый", "четырнадцатый", "пятнадцатый",
1159
- "шестнадцатый", "семнадцатый", "восемнадцатый", "девятнадцатый", "двадцатый"
 
1160
  ]
1161
-
1162
- words = message.split()
1163
- lemmas = [morph.parse(word)[0].normal_form for word in words]
1164
-
1165
- for word in ordinal_words:
1166
- if word in lemmas:
1167
- print(f"Не пройдена проверка: Порядковые числительные от 10 прописью. Сообщение: {message}")
1168
- return False, f'Не пройдена проверка на порядковые числительные: {word}'
1169
  return True
1170
 
1171
 
@@ -1196,99 +1307,152 @@ def check_no_subordinate_clauses_chain(message):
1196
 
1197
  # 12. Разделительные повторяющиеся союзы
1198
 
1199
- def check_no_repeating_conjunctions(message):
1200
- # Регулярное выражение для поиска разделительных повторяющихся союзов с запятой перед вторым союзом
1201
- repeating_conjunctions_patterns = r'\b(и|ни|то|не то|или|либо)\b\s*(.*?)\s*,\s*\b\1\b'
 
 
 
 
 
1202
 
1203
- # Разделяем сообщение на предложения по точке, вопросительному и восклицательному знакам
1204
  sentences = re.split(r'[.!?]\s*', message)
1205
-
1206
- # Проверяем каждое предложение отдельно
1207
- for sentence in sentences:
1208
- if re.search(repeating_conjunctions_patterns, sentence, re.IGNORECASE):
1209
- print(f"Не пройдена проверка: Разделительные повторяющиеся союзы. Сообщение: {message}")
1210
- return False, f'Не пройдена проверка на разделительные повторяющиеся союзы: {sentence}'
1211
  return True
1212
 
1213
 
1214
  # 13. Вводные конструкции
1215
 
1216
- def check_no_introductory_phrases(message):
1217
- introductory_phrases = [
 
 
 
 
 
 
 
 
1218
  r'\b(во-первых|во-вторых|с одной стороны|по сути|по правде говоря)\b',
1219
  r'\b(может быть|кстати|конечно|естественно|безусловно|возможно)\b'
1220
  ]
1221
-
1222
- for pattern in introductory_phrases:
1223
- if re.search(pattern, message, re.IGNORECASE):
1224
- print(f"Не пройдена проверка: Вводные конструкции. Сообщение: {message}")
1225
- return False, f'Не пройдена проверка на вводные конструкции: {pattern}'
 
1226
  return True
1227
 
1228
 
1229
  # 14. Усилители
1230
 
1231
- def check_no_amplifiers(message):
1232
- amplifiers = [
1233
- r'\b(очень|крайне|чрезвычайно|совсем|полностью|чисто)\b'
1234
- ]
 
 
 
 
1235
 
1236
- for pattern in amplifiers:
1237
- if re.search(pattern, message, re.IGNORECASE):
1238
- print(f"Не пройдена проверка: Усилители. Сообщение: {message}")
1239
- return False, f"Не пройдена проверка на усилители: {pattern}"
 
 
 
 
1240
  return True
1241
 
1242
  # 15. Паразиты времени
1243
 
1244
- def check_no_time_parasites(message):
1245
- time_parasites = [
1246
- r'\b(немедленно|срочно|в данный момент)\b'
1247
- ]
 
 
 
 
1248
 
1249
- for pattern in time_parasites:
1250
- if re.search(pattern, message, re.IGNORECASE):
1251
- print(f"Не пройдена проверка: Паразиты времени. Сообщение: {message}")
1252
- return False, f'Не пройдена проверка на паразитов времени: {pattern}'
 
 
 
 
1253
  return True
1254
 
1255
 
1256
  # 16. Несколько существительных подряд
1257
 
1258
- def check_no_multiple_nouns(message):
1259
- noun_count = 0
1260
- words = re.split(r'\s+|[.!?]', message) # Разбиваем по пробелам и знакам препинания
1261
- morph = pymorphy3.MorphAnalyzer()
 
 
 
 
1262
 
1263
- for word in range(len(words)):
1264
- parsed_word = morph.parse(words[word])[0]
 
 
1265
 
1266
- # Если слово — существительное
1267
- if 'NOUN' in parsed_word.tag:
1268
- noun_count += 1
1269
- # Если встречен конец предложения (точка, вопросительный знак, восклицательный знак)
1270
- elif re.match(r'[.!?]', words[word]):
1271
- noun_count = 0
 
 
 
1272
  else:
1273
- noun_count = 0
 
1274
 
1275
- if noun_count > 2:
1276
- print(f"Не пройдена проверка: Несколько существительных подряд. Сообщение: {message}")
1277
- return False, f'Не пройдена проверка на несколько существительных подряд: {words[word - 2: word + 1]}'
 
1278
  return True
1279
 
1280
 
1281
  # 17. Производные предлоги
1282
 
1283
- def check_no_derived_prepositions(message):
1284
- derived_prepositions = [
1285
- r'\b(в течение|в ходе|вследствие|в связи с|по мере|при помощи|согласно|вопреки|на основании|на случай|в продолжение|по причине|вблизи|вдалеке|вокруг|внутри|вдоль|посередине|вне|снаружи|благодаря|невзирая на|исходя из|благодаря)\b'
1286
- ]
1287
-
1288
- for pattern in derived_prepositions:
1289
- if re.search(pattern, message, re.IGNORECASE):
1290
- print(f"Не пройдена проверка: Производные предлоги. Сообщение: {message}")
1291
- return False, f"Не пройдена проверка на производные предлоги: {pattern}"
 
 
 
 
 
 
 
 
 
 
 
 
1292
  return True
1293
 
1294
 
@@ -1312,84 +1476,78 @@ def check_no_compound_sentences(message):
1312
 
1313
  # 20. Даты прописью
1314
 
1315
- def check_no_dates_written_out(message):
1316
- # Ищем упоминания месяцев или слов, связанных с датами
 
 
 
 
 
 
 
 
 
1317
  months = [
1318
  "января", "февраля", "марта", "апреля", "мая", "июня",
1319
  "июля", "августа", "сентября", "октября", "ноября", "декабря"
1320
  ]
1321
-
1322
- # Слова для проверки чисел прописью
1323
- date_written_out_patterns = [
1324
- r'\b(первого|второго|третьего|четвертого|пятого|шестого|седьмого|восьмого|девятого|десятого|одиннадцатого|двенадцатого|тринадцатого|четырнадцатого|пятнадцатого|шестнадцатого|семнадцатого|восемнадцатого|девятнадцатого|двадцатого|двадцать первого|двадцать второго|двадцать третьего|двадцать четвертого|двадцать пятого|двадцать шестого|двадцать седьмого|двадцать восьмого|двадцать девятого|тридцатого|тридцать первого)\b'
 
 
 
 
 
1325
  ]
1326
 
1327
- for month in months:
1328
- for pattern in date_written_out_patterns:
1329
- if re.search(f'{pattern}\\s{month}', message, re.IGNORECASE):
1330
- print(f"Не пройдена проверка: Даты прописью. Сообщение: {message}")
1331
- return False, f"Не пройдена проверка на даты прописью: {pattern}"
1332
-
 
 
 
 
1333
  return True
1334
 
1335
  # Доп правило. Повторы слов
1336
 
1337
- def check_no_word_repetitions(message, key_message):
1338
- morph = pymorphy3.MorphAnalyzer()
1339
-
1340
- # Определяем набор частей речи, которые будем игнорировать
1341
- ignore_pos = {
1342
- 'PREP', # Предлоги
1343
- 'CONJ', # Союзы
1344
- 'PRON', # Местоимения
1345
- 'INTJ', # Междометия
1346
- 'NUMR', # Числительные
1347
- 'PART', # Частицы
1348
- 'NPRO'
1349
- }
1350
 
1351
- # Разбиваем текст на слова, учитывая составные слова с дефисом
1352
- words = re.findall(r'\b\w+(?:-\w+)*\b', message.lower())
1353
 
1354
- # Функция для нормализации слов и получения их базовых форм
1355
- def normalize_word(word):
1356
- parses = morph.parse(word)
1357
- if not parses:
1358
- return word # Если слово не распознано, возвращаем как есть
1359
- parse = parses[0]
1360
- return parse.normal_form, parse.tag.POS
1361
 
1362
- # Нормализуем ключевое сообщение и собираем его базовые формы
1363
  key_normalized = set()
1364
- for word in re.findall(r'\b\w+\b', key_message.lower()):
1365
- norm, pos = normalize_word(word)
1366
- key_normalized.add(norm)
1367
-
1368
- # Добавляем базовые формы ключевого сообщения в игнорируемые слова
1369
- # Это позволяет игнорировать повторения слов из ключевого сообщения
1370
- # Кроме того, игнорируем слова из определенных частей речи
1371
- normalized_words = {}
1372
-
1373
- for word in words:
1374
- norm, pos = normalize_word(word)
1375
-
1376
- # Игнорируем слово, если оно относится к одной из игнорируемых частей речи
1377
- if pos in ignore_pos:
1378
  continue
1379
-
1380
- # Игнорируем слово, если оно присутствует в ключевом сообщении
1381
- if norm in key_normalized:
1382
  continue
1383
-
1384
- # Если слово уже встречалось, возвращаем False
1385
- if norm in normalized_words:
1386
- print(f"Не пройдена проверка: Повторы слов. Сообщение: {message}")
1387
- return False, f"Не пройдена проверка на повторы слов: {norm}"
1388
-
1389
- # Добавляем слово в словарь для отслеживания повторов
1390
- normalized_words[norm] = True
1391
-
1392
- # Если повторов не найдено, возвращаем True
1393
  return True
1394
 
1395
  # ФУНКЦИИ ПРОВЕРОК (КОНЕЦ)
 
909
 
910
  def check_source_fields(description, product_name, benefits, key_message):
911
  results = []
912
+ exceptions_dict = {
913
+ "forbidden_words": set(),
914
+ "greetings": set(),
915
+ "promises": set(),
916
+ "double_verbs": set(),
917
+ "participles": set(),
918
+ "adverbial_participles": set(),
919
+ "superlative_adjectives": set(),
920
+ "passive_voice": set(),
921
+ "written_out_ordinals": set(),
922
+ "repeating_conjunctions": set(),
923
+ "introductory_phrases": set(),
924
+ "amplifiers": set(),
925
+ "time_parasites": set(),
926
+ "multiple_nouns": set(),
927
+ "derived_prepositions": set(),
928
+ "compound_sentences": set(),
929
+ "dates_written_out": set(),
930
+ "word_repetitions": set()
931
+ }
932
 
933
  # Проверяем "Описание предложения"
934
  desc_checks = perform_checks(description, "")
935
+ not_passed_desc = extract_failed_checks(desc_checks, exceptions_dict, context="Описание предложения")
936
  if not_passed_desc:
937
  results.append(f"Описание предложения:\n{not_passed_desc}")
938
 
939
  # Проверяем "Наименование продукта"
940
  name_checks = perform_checks(product_name, "")
941
+ not_passed_name = extract_failed_checks(name_checks, exceptions_dict, context="Наименование продукта")
942
  if not_passed_name:
943
  results.append(f"Наименование продукта:\n{not_passed_name}")
944
 
945
  # Проверяем "Преимущества"
946
  ben_checks = perform_checks(benefits, "")
947
+ not_passed_ben = extract_failed_checks(ben_checks, exceptions_dict, context="Преимущества")
948
  if not_passed_ben:
949
  results.append(f"Преимущества:\n{not_passed_ben}")
950
 
951
  # Проверяем "Ключевое сообщение"
952
  km_checks = perform_checks(key_message, "")
953
+ not_passed_km = extract_failed_checks(km_checks, exceptions_dict, context="Ключевое сообщение")
954
  if not_passed_km:
955
  results.append(f"Ключевое сообщение:\n{not_passed_km}")
956
 
957
  if not results:
958
+ return "Проверка исходных данных пройдена", exceptions_dict
959
  else:
960
+ report = "\n\n".join(results)
961
+ return report, exceptions_dict
962
 
963
 
964
  def on_check_source_fields(description, product_name, benefits, key_message):
 
969
 
970
 
971
  def extract_failed_checks(checks_dict):
972
+
973
+ morph = pymorphy3.MorphAnalyzer()
974
+
975
  lines = []
976
+
977
+ def lemma_pair(word1, word2):
978
+ p1 = morph.parse(word1)[0].normal_form
979
+ p2 = morph.parse(word2)[0].normal_form
980
+ return (p1, p2)
981
+
982
  for rule_key, result in checks_dict.items():
983
  # Определяем, было ли нарушение
984
  if isinstance(result, tuple):
 
1019
 
1020
  # ФУНКЦИИ ПРОВЕРОК (НАЧАЛО)
1021
 
1022
+ def lemmatize_word(word, morph):
1023
+ """
1024
+ Возвращает (lemma, POS) для переданного слова.
1025
+ """
1026
+ parsed = morph.parse(word)
1027
+ if not parsed:
1028
+ return word, None
1029
+ best = parsed[0]
1030
+ return best.normal_form, best.tag.POS
1031
+
1032
  # 1. Запрещенные слова
1033
 
1034
+ def check_forbidden_words(message, exceptions=None):
1035
+ """
1036
+ Проверка на запрещённые слова.
1037
+ Если лемма «запрещённого слова» находится в exceptions['forbidden_words'],
1038
+ то пропускаем.
1039
+ """
1040
+ if exceptions is None:
1041
+ exceptions = {}
1042
+ allowed_lemmas = exceptions.get("forbidden_words", set())
1043
 
1044
+ morph = pymorphy3.MorphAnalyzer()
1045
  forbidden_patterns = [
1046
  r'№\s?1\b', r'номер\sодин\b', r'номер\s1\b',
1047
  r'вкусный', r'дешёвый', r'продукт',
 
1050
  r'гарантия', r'успех', r'лидер', 'никакой'
1051
  ]
1052
 
1053
+ # Удаляем пунктуацию
1054
+ message_no_punct = message.translate(str.maketrans('', '', string.punctuation))
1055
 
1056
+ # Пример: «бессроч» => placeholder
1057
  placeholder = "заменабессроч"
1058
+ message_no_punct = re.sub(r'\b\w*бессроч\w*\b', placeholder, message_no_punct, flags=re.IGNORECASE)
 
 
 
 
 
 
 
 
 
1059
 
1060
+ # Лемматизируем все слова
1061
+ words = message_no_punct.split()
1062
+ lemmas = [morph.parse(w)[0].normal_form for w in words]
1063
+ lemmas = [re.sub(r'заменабессроч', 'бессроч', l) for l in lemmas]
1064
+ normalized_msg = ' '.join(lemmas)
1065
 
1066
+ # Для каждого pattern проверяем, нет ли совпадения
1067
  for pattern in forbidden_patterns:
1068
+ found = re.search(pattern, normalized_msg, re.IGNORECASE)
1069
+ if found:
1070
+ # Получим саму найденную строку
1071
+ matched_str = found.group(0)
1072
+ # Лемматизируем
1073
+ lemma_found, _ = lemmatize_word(matched_str, morph)
1074
+ if lemma_found not in allowed_lemmas:
1075
+ return False, f"Запрещенное слово: {matched_str}"
1076
 
1077
  return True
1078
 
1079
 
1080
  # 2 и #3. Обращение к клиенту и приветствие клиента
1081
 
1082
+ def check_no_greeting(message, exceptions=None):
1083
+ """
1084
+ Проверка на «приветствия».
1085
+ Если лемма слова среди exceptions['greetings'], пропускаем.
1086
+ """
1087
+ if exceptions is None:
1088
+ exceptions = {}
1089
+ allowed_lemmas = exceptions.get("greetings", set())
1090
+
1091
  greeting_patterns = [
1092
  r"привет\b", r"здравствуй", r"добрый\s(день|вечер|утро)",
1093
  r"дорогой\b", r"уважаемый\b", r"дорогая\b", r"уважаемая\b",
1094
  r"господин\b", r"госпожа\b", r"друг\b", r"коллега\b",
1095
+ r"товарищ\b", r"приятель\b", r"подруга\b"
1096
  ]
1097
+ # Будем искать все совпадения паттернов
1098
+ for pat in greeting_patterns:
1099
+ match = re.search(pat, message, re.IGNORECASE)
1100
+ if match:
1101
+ found = match.group(0).lower() # «дорогая», «привет» и т.п.
1102
+ morph = pymorphy3.MorphAnalyzer()
1103
+ lemma, pos = lemmatize_word(found, morph)
1104
+ if lemma not in allowed_lemmas:
1105
+ return False, f"Есть приветствие: {found}"
1106
  return True
1107
 
1108
 
1109
  # 4. Обещания и гарантии
1110
 
1111
+ def check_no_promises(message, exceptions=None):
1112
+ """
1113
+ Проверка на «обещания».
1114
+ Если lemma слова в exceptions['promises'], то пропускаем.
1115
+ """
1116
+ if exceptions is None:
1117
+ exceptions = {}
1118
+ allowed_lemmas = exceptions.get("promises", set())
1119
+
1120
  morph = pymorphy3.MorphAnalyzer()
1121
+ patterns = ["обещать", "обещание", "гарантировать", "обязаться", "обязать", "обязательство", "обязательный"]
 
 
1122
 
1123
  words = message.split()
1124
+ lemmas = [morph.parse(w)[0].normal_form for w in words]
1125
 
1126
+ for patt in patterns:
1127
+ if patt in lemmas:
1128
+ if patt not in allowed_lemmas:
1129
+ return False, f"Не пройдена проверка: обещания => {patt}"
1130
  return True
1131
 
1132
 
1133
  # 5. Составные конструкции из двух глаголов
1134
 
1135
+ def check_no_double_verbs(message, exceptions=None):
1136
+ """
1137
+ Проверка на 2 подряд глагола.
1138
+ Если (lemma1, lemma2) находится в exceptions['double_verbs'], то разрешаем.
1139
+ """
1140
+ if exceptions is None:
1141
+ exceptions = {}
1142
+ allowed_pairs = exceptions.get("double_verbs", set())
1143
+
1144
  morph = pymorphy3.MorphAnalyzer()
 
1145
  words = re.split(r'\s+|[.!?]', message)
1146
+
1147
+ tokens = [w.strip() for w in words if w.strip()]
1148
+ parses = [morph.parse(tok)[0] for tok in tokens]
1149
+
1150
+ for i in range(len(parses) - 1):
1151
+ if (parses[i].tag.POS in {'VERB', 'INFN'}) and (parses[i+1].tag.POS in {'VERB', 'INFN'}):
1152
+ lemma1 = parses[i].normal_form
1153
+ lemma2 = parses[i+1].normal_form
1154
+ pair = (lemma1, lemma2)
1155
+ # Если разрешено
1156
+ if pair in allowed_pairs:
1157
+ continue
1158
+ # Если это "хотеть", "начинать", ...
1159
+ if lemma1 in ["хотеть", "начинать", "начать"]:
1160
+ continue
1161
+ return False, f"Не пройдена проверка на 2 глагола подряд: {parses[i].word} {parses[i+1].word}"
1162
  return True
1163
 
1164
 
1165
  # 6. Причастия и причастные обороты
1166
 
1167
+ def check_no_participles(message, exceptions=None):
1168
+ """
1169
+ Проверка на причастия.
1170
+ Если lemma причастия в exceptions['participles'], разрешаем.
1171
+ """
1172
+ if exceptions is None:
1173
+ exceptions = {}
1174
+ allowed_lemmas = exceptions.get("participles", set())
1175
+
1176
+ skip_lemmas = {"повысить", "увеличить", "понизить", "снизить"}
1177
+
1178
  morph = pymorphy3.MorphAnalyzer()
1179
  words = message.split()
1180
+
1181
+ for w in words:
1182
+ p = morph.parse(w)[0]
1183
+ lemma = p.normal_form
1184
+ if 'PRTF' in p.tag:
1185
+ # Проверяем исключения
1186
+ if lemma not in skip_lemmas and lemma not in allowed_lemmas:
1187
+ return False, f"Не пройдена проверка на причастие: {p.word}"
1188
  return True
1189
 
1190
 
1191
  # 7. Деепричастия и деепричастные обороты
1192
 
1193
+ def check_no_adverbial_participles(message, exceptions=None):
1194
+ """
1195
+ Проверка на деепричастия.
1196
+ Если lemma в exceptions['adverbial_participles'], то не считае�� нарушением.
1197
+ """
1198
+ if exceptions is None:
1199
+ exceptions = {}
1200
+ allowed_lemmas = exceptions.get("adverbial_participles", set())
1201
+
1202
  morph = pymorphy3.MorphAnalyzer()
1203
  words = message.split()
1204
+ for w in words:
1205
+ p = morph.parse(w)[0]
1206
+ lemma = p.normal_form
1207
+ if "GRND" in p.tag:
1208
+ if lemma not in allowed_lemmas:
1209
+ return False, f"Не пройдена проверка: деепричастие => {p.word}"
1210
  return True
1211
 
1212
 
1213
  # 8. Превосходная степень прилагательных
1214
 
1215
+ def check_no_superlative_adjectives(message, exceptions=None):
1216
+ """
1217
+ Проверка на превосходную степень прилагательных.
1218
+ Если lemma прилагательного среди exceptions['superlative_adjectives'], разрешаем.
1219
+ """
1220
+ if exceptions is None:
1221
+ exceptions = {}
1222
+ allowed_lemmas = exceptions.get("superlative_adjectives", set())
1223
 
1224
+ morph = pymorphy3.MorphAnalyzer()
1225
+ for w in message.split():
1226
+ p = morph.parse(w)[0]
1227
+ lemma = p.normal_form
1228
+ if 'Supr' in p.tag:
1229
+ if lemma not in allowed_lemmas:
1230
+ return False, f"Не пройдена проверка на превосходную степень: {p.word}"
1231
  return True
1232
 
1233
 
1234
  # 9. Страдательный залог
1235
 
1236
+ def check_no_passive_voice(message, exceptions=None):
1237
+ """
1238
+ Проверка на страдательный залог.
1239
+ Если lemma в exceptions['passive_voice'], пропускаем.
1240
+ """
1241
+ if exceptions is None:
1242
+ exceptions = {}
1243
+ allowed_lemmas = exceptions.get("passive_voice", set())
1244
+
1245
  morph = pymorphy3.MorphAnalyzer()
 
1246
  words = re.findall(r'\b\w+(?:-\w+)*\b', message.lower())
1247
 
1248
+ for w in words:
1249
+ p = morph.parse(w)[0]
1250
+ lemma = p.normal_form
1251
+ if 'pssv' in p.tag:
1252
+ if lemma not in allowed_lemmas:
1253
+ return False, f"Не пройдена проверка на страдательный залог: {w}"
1254
  return True
1255
 
1256
 
1257
  # 10. Порядковые числительные от 10 прописью
1258
 
1259
+ def check_no_written_out_ordinals(message, exceptions=None):
1260
+ """
1261
+ Проверка на порядковые числительные, написанные прописью (десятый и т.д.).
1262
+ Если lemma в exceptions['written_out_ordinals'], пропускаем.
1263
+ """
1264
+ if exceptions is None:
1265
+ exceptions = {}
1266
+ allowed_lemmas = exceptions.get("written_out_ordinals", set())
1267
+
1268
  morph = pymorphy3.MorphAnalyzer()
1269
  ordinal_words = [
1270
+ "десятый", "одиннадцатый", "двенадцатый", "тринадцатый",
1271
+ "четырнадцатый", "пятнадцатый", "шестнадцатый", "семнадцатый",
1272
+ "восемнадцатый", "девятнадцатый", "двадцатый"
1273
  ]
1274
+ tokens = message.split()
1275
+ lemmas = [morph.parse(t)[0].normal_form for t in tokens]
1276
+ for ow in ordinal_words:
1277
+ if ow in lemmas:
1278
+ if ow not in allowed_lemmas:
1279
+ return False, f"Не пройдена проверка на порядковые числительные: {ow}"
 
 
1280
  return True
1281
 
1282
 
 
1307
 
1308
  # 12. Разделительные повторяющиеся союзы
1309
 
1310
+ def check_no_repeating_conjunctions(message, exceptions=None):
1311
+ """
1312
+ Проверка на повторяющиеся союзы 'и', 'или' и т.п.
1313
+ Если сам союз (в лемме) в exceptions['repeating_conjunctions'], пропускаем.
1314
+ """
1315
+ if exceptions is None:
1316
+ exceptions = {}
1317
+ allowed_conjs = exceptions.get("repeating_conjunctions", set())
1318
 
1319
+ pattern = re.compile(r'\b(и|ни|то|не то|или|либо)\b\s*(.*?)\s*,\s*\b\1\b', re.IGNORECASE)
1320
  sentences = re.split(r'[.!?]\s*', message)
1321
+ for s in sentences:
1322
+ m = pattern.search(s)
1323
+ if m:
1324
+ conj = m.group(1).lower()
1325
+ if conj not in allowed_conjs:
1326
+ return False, f"Не пройдена проверка на повторяющиеся союзы: {s}"
1327
  return True
1328
 
1329
 
1330
  # 13. Вводные конструкции
1331
 
1332
+ def check_no_introductory_phrases(message, exceptions=None):
1333
+ """
1334
+ Проверка на вводные конструкции.
1335
+ Если exact фраза в exceptions['introductory_phrases'], пропускаем.
1336
+ """
1337
+ if exceptions is None:
1338
+ exceptions = {}
1339
+ allowed_phrases = exceptions.get("introductory_phrases", set())
1340
+
1341
+ patterns = [
1342
  r'\b(во-первых|во-вторых|с одной стороны|по сути|по правде говоря)\b',
1343
  r'\b(может быть|кстати|конечно|естественно|безусловно|возможно)\b'
1344
  ]
1345
+ for pat in patterns:
1346
+ match = re.search(pat, message, re.IGNORECASE)
1347
+ if match:
1348
+ found = match.group(1).lower()
1349
+ if found not in allowed_phrases:
1350
+ return False, f"Не пройдена проверка на вводные конструкции: {found}"
1351
  return True
1352
 
1353
 
1354
  # 14. Усилители
1355
 
1356
+ def check_no_amplifiers(message, exceptions=None):
1357
+ """
1358
+ Проверка на усилители (очень, крайне...).
1359
+ Если лемма в exceptions['amplifiers'], пропускаем.
1360
+ """
1361
+ if exceptions is None:
1362
+ exceptions = {}
1363
+ allowed_lemmas = exceptions.get("amplifiers", set())
1364
 
1365
+ pattern = re.compile(r'\b(очень|крайне|чрезвычайно|совсем|полностью|чисто)\b', re.IGNORECASE)
1366
+ matches = pattern.findall(message)
1367
+ if matches:
1368
+ morph = pymorphy3.MorphAnalyzer()
1369
+ for m in matches:
1370
+ lemma, _ = lemmatize_word(m, morph)
1371
+ if lemma not in allowed_lemmas:
1372
+ return False, f"Не пройдена проверка на усилители: {m}"
1373
  return True
1374
 
1375
  # 15. Паразиты времени
1376
 
1377
+ def check_no_time_parasites(message, exceptions=None):
1378
+ """
1379
+ Проверка на «паразиты времени» (немедленно, срочно...).
1380
+ Если лемма в exceptions['time_parasites'], пропускаем.
1381
+ """
1382
+ if exceptions is None:
1383
+ exceptions = {}
1384
+ allowed_lemmas = exceptions.get("time_parasites", set())
1385
 
1386
+ pattern = re.compile(r'\b(немедленно|срочно|в данный момент)\b', re.IGNORECASE)
1387
+ matches = pattern.findall(message)
1388
+ if matches:
1389
+ morph = pymorphy3.MorphAnalyzer()
1390
+ for m in matches:
1391
+ lemma, _ = lemmatize_word(m, morph)
1392
+ if lemma not in allowed_lemmas:
1393
+ return False, f"Не пройдена проверка на паразитов времени: {m}"
1394
  return True
1395
 
1396
 
1397
  # 16. Несколько существительных подряд
1398
 
1399
+ def check_no_multiple_nouns(message, exceptions=None):
1400
+ """
1401
+ Проверка на 3+ подряд существительных.
1402
+ Если конкретная цепочка лемм в exceptions['multiple_nouns'], пропускаем.
1403
+ """
1404
+ if exceptions is None:
1405
+ exceptions = {}
1406
+ allowed_chains = exceptions.get("multiple_nouns", set()) # set of tuples
1407
 
1408
+ morph = pymorphy3.MorphAnalyzer()
1409
+ tokens = re.split(r'\s+|[.!?]', message)
1410
+ chain = []
1411
+ count = 0
1412
 
1413
+ for t in tokens:
1414
+ t = t.strip()
1415
+ if not t:
1416
+ continue
1417
+ p = morph.parse(t)[0]
1418
+ lemma = p.normal_form
1419
+ if 'NOUN' in p.tag:
1420
+ count += 1
1421
+ chain.append(lemma)
1422
  else:
1423
+ count = 0
1424
+ chain = []
1425
 
1426
+ if count > 2:
1427
+ chain_tuple = tuple(chain) # например ('зачисление', 'зарплата', 'сотрудникам')
1428
+ if chain_tuple not in allowed_chains:
1429
+ return False, f"Несколько существительных подряд: {chain_tuple}"
1430
  return True
1431
 
1432
 
1433
  # 17. Производные предлоги
1434
 
1435
+ def check_no_derived_prepositions(message, exceptions=None):
1436
+ """
1437
+ Проверка на производные предлоги.
1438
+ Если конкретный предлог в exceptions['derived_prepositions'], пропускаем.
1439
+ """
1440
+ if exceptions is None:
1441
+ exceptions = {}
1442
+ allowed_preps = exceptions.get("derived_prepositions", set())
1443
+
1444
+ pattern_text = (r'\b(в течение|в ходе|вследствие|в связи с|по мере|при помощи|'
1445
+ r'согласно|вопреки|на основании|на случай|в продолжение|по причине|'
1446
+ r'вблизи|вдалеке|вокруг|внутри|вдоль|посередине|вне|снаружи|'
1447
+ r'благодаря|невзирая на|исходя из|благодаря)\b')
1448
+ pat = re.compile(pattern_text, re.IGNORECASE)
1449
+
1450
+ matches = pat.findall(message)
1451
+ if matches:
1452
+ for m in matches:
1453
+ low = m.lower()
1454
+ if low not in allowed_preps:
1455
+ return False, f"Не пройдена проверка на производные предлоги: {m}"
1456
  return True
1457
 
1458
 
 
1476
 
1477
  # 20. Даты прописью
1478
 
1479
+ def check_no_dates_written_out(message, exceptions=None):
1480
+ """
1481
+ Проверка на даты прописью.
1482
+ Если (lemma_ordinal, lemma_month) в exceptions['dates_written_out'], пропускаем.
1483
+ """
1484
+ if exceptions is None:
1485
+ exceptions = {}
1486
+ allowed_dates = exceptions.get("dates_written_out", set())
1487
+
1488
+ morph = pymorphy3.MorphAnalyzer()
1489
+
1490
  months = [
1491
  "января", "февраля", "марта", "апреля", "мая", "июня",
1492
  "июля", "августа", "сентября", "октября", "ноября", "декабря"
1493
  ]
1494
+ date_patterns = [
1495
+ r'\b(первого|второго|третьего|четвертого|пятого|шестого|седьмого|'
1496
+ r'восьмого|девятого|десятого|одиннадцатого|двенадцатого|'
1497
+ r'тринадцатого|четырнадцатого|пятнадцатого|шестнадцатого|'
1498
+ r'семнадцатого|восемнадцатого|девятнадцатого|двадцатого|'
1499
+ r'двадцать первого|двадцать второго|двадцать третьего|'
1500
+ r'двадцать четвертого|двадцать пятого|двадцать шестого|'
1501
+ r'двадцать седьмого|двадцать восьмого|двадцать девятого|'
1502
+ r'тридцатого|тридцать первого)\b'
1503
  ]
1504
 
1505
+ for m in months:
1506
+ for patt in date_patterns:
1507
+ found = re.search(f"{patt}\\s{m}", message, re.IGNORECASE)
1508
+ if found:
1509
+ ordinal_str = found.group(1).lower() # например «пятнадцатого»
1510
+ lemma_ord, _ = lemmatize_word(ordinal_str, morph)
1511
+ lemma_month, _ = lemmatize_word(m, morph)
1512
+ pair = (lemma_ord, lemma_month) # («пятнадцатый», «июль»)
1513
+ if pair not in allowed_dates:
1514
+ return False, f"Не пройдена проверка на даты прописью: {found.group(0)}"
1515
  return True
1516
 
1517
  # Доп правило. Повторы слов
1518
 
1519
+ def check_no_word_repetitions(message, key_message, exceptions=None):
1520
+ """
1521
+ Проверка на повторы слов (кроме определённых частей речи).
1522
+ Если lemma есть в exceptions['word_repetitions'], пропускаем.
1523
+ """
1524
+ if exceptions is None:
1525
+ exceptions = {}
1526
+ allowed_lemmas = exceptions.get("word_repetitions", set())
 
 
 
 
 
1527
 
1528
+ morph = pymorphy3.MorphAnalyzer()
1529
+ ignore_pos = {'PREP', 'CONJ', 'PRON', 'INTJ', 'NUMR', 'PART', 'NPRO'}
1530
 
1531
+ msg_words = re.findall(r'\b\w+(?:-\w+)*\b', message.lower())
 
 
 
 
 
 
1532
 
1533
+ # Ключевое сообщение
1534
  key_normalized = set()
1535
+ for kw in re.findall(r'\b\w+\b', key_message.lower()):
1536
+ lemma_k, pos_k = lemmatize_word(kw, morph)
1537
+ key_normalized.add(lemma_k)
1538
+
1539
+ seen = {}
1540
+ for w in msg_words:
1541
+ lemma, pos = lemmatize_word(w, morph)
1542
+ if (not pos) or (pos in ignore_pos):
 
 
 
 
 
 
1543
  continue
1544
+ if lemma in key_normalized:
 
 
1545
  continue
1546
+ if lemma in allowed_lemmas:
1547
+ continue
1548
+ if lemma in seen:
1549
+ return False, f"Не пройдена проверка на повторы слов: {lemma}"
1550
+ seen[lemma] = True
 
 
 
 
 
1551
  return True
1552
 
1553
  # ФУНКЦИИ ПРОВЕРОК (КОНЕЦ)